Home > C++ > C++11: About Lambda Functions

C++11: About Lambda Functions

September 22, 2012 Leave a comment Go to comments

One of my favourite inclusions in C++11 – the latest iteration of C++, formerly known as C++0x – is the ability to create lambda functions. Lambda functions are nothing more than unnamed, essentially anonymous functions that you can define in place of where you would usually use a function name, pointer or reference, and they come in particularly handy in combination with STL, although they are certainly not limited to that use. If you have become used to using Boost.Lambda in your applications, C++11’s lambda functions provide a more convenient syntax while also eliminating your code’s dependency on Boost.Lambda.

As with function pointers and references, lambda functions can be defined as variables, passed to and returned from other functions (including other lambda functions) and so on.

Ground Rules

Let us begin with a simple example comparing the use of C++11 and Boost.Lambda when iterating over a few numbers in an STL vector:

/* Anonymous functions */

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/if.hpp>
#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
	std::vector<int> v;
	v.push_back(1);
	v.push_back(3);
	v.push_back(2);

	// boost::lambda::_1 substitutes the first argument
	// can't use std::endl instead of \n because of type issues (see boost c++ libraries page 27)
	std::for_each(v.begin(), v.end(), std::cout << boost::lambda::_1 << "\n");

	std::cout << std::endl;

	// also available: if_then_else, if_then_else_return
	std::for_each(v.begin(), v.end(),
		boost::lambda::if_then(boost::lambda::_1 > 1,
		std::cout << boost::lambda::_1 << "\n"));

	std::cout << std::endl;

	// Visual C++ 2010 version (no Boost.Lambda required - but not part of standard yet)

	std::for_each(v.begin(), v.end(),
		[] (int i) { if (i > 1) std::cout << i << std::endl; });
}

First we populate the vector with the numbers 1, 3, 2 in that order. We then print them out using three different techniques:

  • Technique 1 uses Boost to create a lambda function. The for each loop provides the value of the current vector item as the first argument to the function, and the _1 placeholder captures the value of this argument. Thus, the numbers are printed in sequence.
  • Technique 2 uses the more complex if_then structure to only print numbers greater than 1, once again in order. This prints 3, 2.
  • Technique 3 reproduces the results of technique 2 in much simpler terms. We use a C++11 lambda function which receives the current vector item value in i and prints it if greater than 1. This prints 3, 2.

I think it would be generally agreed that the last version is both the most readable and the most flexible. Let’s have a look at the lambda function definition in more detail:

[] (int i) { if (i > 1) std::cout << i << std::endl; }

The square brackets indicate that we will now define a lambda function. The bracketed part which follows is the list of arguments to be received by the function – this can be omitted altogether if there are no arguments (a set of () brackets on its own is not required as it is in a regular function definition). Finally there is the regular body of code inside the braces.

Apart from readability, what is the benefit? Well, if you are only going to use a function once, and particularly if it a short function, it is rather cumbersome to define it separately in the code when its only real relevance is to the one place where you use it. We could have equally used a function object like this for technique 3:

void printGreaterThanOne(int i)
{
    if (i > 1)
        std::cout << i << std::endl;
}
...
std::for_each(v.begin(), v.end(), printGreaterThanOne);

but that is less convenient and less readable.

Capture Semantics

Let us take a slightly more tricky example:

#include <boost/lambda/lambda.hpp>
#include <boost/bind.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>

void pb(std::string s, std::vector<int> &sizes)
{
	sizes.push_back(s.size());
}

int main()
{
	std::vector<std::string> strings;

	strings.push_back("Some");
	strings.push_back("Random");
	strings.push_back("Text");

	std::vector<int> sizes;

	// Technique 1: Iterator for loop
	for (std::vector<std::string>::iterator it = strings.begin(); it != strings.end(); ++it)
		sizes.push_back(it->size());

        // Technique 2: Boost.Bind with function object
	std::for_each(strings.begin(), strings.end(), boost::bind(pb, _1, boost::ref(sizes)));

	// Technique 3: C++11 Lambda function without capture semantic
	std::transform(strings.begin(), strings.end(), std::back_inserter(sizes), [] (std::string s) -> int { return s.size(); });

        // Technique 4: C++11 Lambda function with capture semantic
	std::for_each(strings.begin(), strings.end(), [&] (std::string s) { sizes.push_back(s.size()); });

        // Print technique 1: Iterator for loop
	for (std::vector<int>::iterator it = sizes.begin(); it != sizes.end(); ++it)
		std::cout << *it << std::endl;

        // Print technique 2: C++11 Lambda function
	std::for_each(sizes.begin(), sizes.end(), [] (int s) { std::cout << s << std::endl; });
}

This code shows four different ways to populate an STL vector with the lengths of strings stored in another STL vector. Let’s take a look:

Technique 1 would be the old-school approach (note in C++11 you can use the auto keyword instead of explicitly specifying the iterator type in the for loop definition, but I have used the more verbose original version here). It simply iterates over the strings vector and pushes the length of each item onto the sizes vector.

Technique 2 uses Boost.Bind to bind each string one at a time to the first argument of the function object pb, together with a reference to the sizes vector so that pb can push the calculated string length onto it.

Technique 3 uses a C++11 lambda function which takes a string and simply returns its length. STL then uses back_inserter to push the returned value into the sizes vector.

Technique 4 uses a C++11 lambda function which does the string length calculation and pushes it onto the sizes vector itself.

Techniques 2 and 4 are analagous here except that technique 4 does not require the sizes vector as a second argument. Instead, it uses a feature called capture semantics to specify that the lambda function would like access to all of the variables in the parent scope, by reference. This is done with the [&] specifier shown above. Parent scope means precisely that: the set of braces which are the immediate parent within which the lambda function is defined. This can be at the class or function scope, or at an arbitrary sub-scope defined within a function (which is a great technique for limiting the lambda function to being able to access only certain variables in the function).

Technique 3 does not capture anything from the parent scope, specified with []. Why does technique 3 not need to capture any variables while technique 4 does? The key difference is that while technique 3’s lambda function merely calculates and returns the size of the string passed as its argument, technique 4 additionally pushes back the value onto the sizes vector, but this is not passed as an argument, therefore it needs to access it from the parent scope. Technique 3’s lambda function does not access anything besides its own argument. As it does not capture anything, if it tried to do so, an error would occur.

Capture Semantics In Detail

As well as no capture ([]) and capture all by reference ([&]), you can also capture variables in the parent scope by value using the [=] specifier. This will naturally prevent those variables from being modified within the function.

In addition, you can specify lists of variables to capture. If a variable is preceded by &, it is captured by reference, otherwise it is captured by value:

[&foo, bar, &baz]

Here, foo and baz are captured by reference, while bar is captured by value. No other variables from the parent scope are accessible.

If you want to be able to access all members of the enclosing class, use:

[this]

This also works if you are passing a lambda function as an argument to a function in a different class – the captured class will be the one the lambda function was originally defined in.

Finally, you can specify that all variables are to be captured by reference or value, with the exception of a specified list, as follows:

[&, a, b, c]

captures everything in the parent scope by reference except for a, b and c which are captured by value.

[=, a, b, c]

captures everything in the parent scope by value except for a, b and c which are captured by reference.

Returning values from a lambda function

As we saw in the earlier examples, lambda functions can return values in the same way as regular functions. The syntax is as follows:
[] (std::string s) -> int { return s.size(); }

ie. you use the construct "-> returntype" after the list of function arguments. If no return type is supplied, it is assumed to be void. Note that in cases where it is clear what the return type should be, it may also be omitted, for example:

int x = [] (int a, int b) { return a + b; }(1, 2);

Here the return type must be an int, so it does not need to be specified.

A Note On Boost.Function

Boost.Function is compatible with C++11 Lambda functions. For example:

struct SomeStruct {
    boost::function<void (Button &)> onClick;
} myStruct;

...
class SomeClass {
  void SomeFunc()
  {
    myStruct.onClick = [this] (Button &b)
    {
      ...
    };
  }
};

works as you would expect.

More Information

I hope you found this brief introduction to C++11 lambda functions useful. There is plenty more to learn about Lambda functions in C++11 and a good starting point is MSDN’s Lambda Expression Syntax page. If you are feeling particularly masochastic, Examples of Lambda Expressions is an excellent and thorough read. Good luck!

Advertisements
  1. September 11, 2014 at 03:29

    Very good information. Lucky me I discovered your site by accident (stumbleupon).
    I havce saved it for later!

  2. Xavier
    April 7, 2015 at 00:57

    Note that along with lambda functions, C++11 has come with std::function.

  1. No trackbacks yet.

Share your thoughts! Note: to post source code, enclose it in [code lang=...] [/code] tags. Valid values for 'lang' are cpp, csharp, xml, javascript, php etc. To post compiler errors or other text that is best read monospaced, use 'text' as the value for lang.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: