Background
This topic is directly related to functions in C++. Functions are the basis for all programming in C and C++. But, for all of the power of functions, they have some glaring short-comings:
A lot of the code used on this web page requires a C++14-compliant compiler. This includes the GNU
g++ compiler (version 5 or later) and the Clang compiler (version 3.4 or later). You will need to
provide this option on the command line: -std=c++14. (This replaces -ansi.)
Depending on the exact version of the compiler, you may have to use -std=c++1y instead.
I tested with g++ version 5.1 and Clang version 3.4 and both accepted -std=c++1y.
Microsoft's Visual Studio 2015 is supposed to be released soon with more support for C++11/14.
Until then, you'll need to use one of the compliant compilers.
Update: Microsoft has
released Visual Studio 2015 and is said to have full C++14 compiliance.
Update 2:
Microsoft's Visual Studio 2017 includes a compiler that is fully C++17 compliant.
First Example
As it turns out, one of the most popular uses for lambda expressions is as parameters to the STL generic algorithms (functions). So, as a first example, let's look at the popular sort algorithm in the STL using a vector of strings:Code:
Output:vector<string> animals {"dog", "stringray", "alligator", "hippopotamus", "mouse", "chihuahua", "yak", "zebra", "uguisu", "rabbit", "cheetah" }; cout << "Original order:" << endl; printc(animals);
There's nothing really notable here, but there are a couple of things to point out:dog stringray alligator hippopotamus mouse chihuahua yak zebra uguisu rabbit cheetah
// Create the container vector<string> animals; // Add items to the container animals.push_back("dog"); animals.push_back("stringray"); animals.push_back("alligator"); animals.push_back("hippopotamus"); animals.push_back("mouse"); animals.push_back("chihuahua"); animals.push_back("yak"); animals.push_back("zebra"); animals.push_back("uguisu"); animals.push_back("rabbit"); animals.push_back("cheetah");
template <typename T> void printc(const T& v) { typename T::const_iterator iter; for (iter = v.begin(); iter != v.end(); ++iter) cout << *iter << " "; cout << endl; }
Output:sort(animals.begin(), animals.end()); cout << "Order by less:" << endl; printc(animals);
Order by less: alligator cheetah chihuahua dog hippopotamus mouse rabbit stringray uguisu yak zebra
The default behavoir of sort is to use the standard function object less. The above call to sort is equivalent to this:
So, if we want to sort the list in decreasing order, we would use greater, which is another pre-defined function object in the STL:sort(animals.begin(), animals.end(), less<string>());
Output:sort(animals.begin(), animals.end(), greater<string>());
A sample implementation of greater:Order by greater: zebra yak uguisu stringray rabbit mouse hippopotamus dog chihuahua cheetah alligator
You can see that the greater class is just a glorified wrapper around the > operator. Most function objects are simplistic like this.template <typename T> class greater { public: bool operator()(const T& a, const T &b) const { return a > b; } };
Here are some other standard function objects that are available in the STL:
But what if we need a custom sort method, say, sorting by string length? We have several options.
Binary function objects Meaning less<type>() greater<type>() equal_to<type>() not_equal_to<type>() less_equal<type>() greater_equal<type>() logical_and<type>() logical_or<type>() plus<type>() minus<type>() multiplies<type>() divides<type>() modulus<type>() lhs < rhs lhs > rhs lhs == rhs lhs != rhs lhs <= rhs lhs >= rhs lhs && rhs lhs || rhs lhs + rhs lhs - rhs lhs * rhs lhs / rhs lhs % rhs
This function will return true if the length of left string is less than the length of the right string and false otherwise. We would call it like this:bool shorter(const string& left, const string& right) { return left.size() < right.size(); }
Output:sort(animals.begin(), animals.end(), shorter);
yak dog zebra mouse uguisu rabbit cheetah stringray chihuahua alligator hippopotamus
We would call it like this:class Shorter { public: bool operator()(const string& left, const string& right) { return left.size() < right.size(); } };
with the output being the same. At least with function objects, you can define the class locally within a function (C++11 or later), giving you kind of the behavior of local or nested functions. You can't do that with a regular function, to wit:sort(animals.begin(), animals.end(), Shorter());
Output:void f1() { vector<string> animals {"dog", "stringray", "alligator", "hippopotamus", "mouse", "chihuahua", "yak", "zebra", "uguisu", "rabbit", "cheetah" // This class can only be local to a function in C++11 or later. // C++98 required it to be at the global scope. class Shorter { public: bool operator()(const string& left, const string& right) { return left.size() < right.size(); } }; cout << "Original order:" << endl; printc(animals); cout << endl; sort(animals.begin(), animals.end(), Shorter()); cout << "Order by length (functor, shortest first)" << endl; printc(animals); cout << endl; }
Still, that's a lot of overhead for something so trivial.Original order: dog stringray alligator hippopotamus mouse chihuahua yak zebra uguisu rabbit cheetah Order by length (functor, shortest first) yak dog zebra mouse uguisu rabbit cheetah stringray chihuahua alligator hippopotamus
This leads to a third alternative:
See how I'm passing the entire function to sort? This, in a nutshell, is what it means to pass a "function" to another function. Well, that's not the exact syntax. Basically, you remove the return type and replace the function name with a pair of brackets:sort(animals.begin(), animals.end(), bool shorter(const string& l, const string& r){return l.size() < r.size();} );
Notes:sort(animals.begin(), animals.end(), [](const string& l, const string& r){return l.size() < r.size();} );
Incidentally, other languages have had this technique for years. The name lambda comes from Lambda Calculus where this technique originated decades ago.
Function Pointers as Parameters
The following is an example that I have used in the past when discussing STL algorithms and function objects. I'm going to repeat it here so that we can compare and contrast with the new version that uses lambda expressions instead.
We will use these simple functions, which should be self-explanatory:
int NextInt() { static int i = 0; return ++i; } |
int RandomInt() { return rand(); } |
void TripleByRef(int& value) { value *= 3; } |
int TripleByVal(int value) { return value * 3; } |
bool DivBy6(int value) { return !(value % 6); } |
bool IsEven(int value) { return !(value % 2); } |
This code uses the functions (pointers, actually) as parameters to the algorithms. Notice the lack of any looping or iteration. The algorithms do the looping for you. And as the wise-man, Bjarne Stroustrup, says: "Prefer algorithms to loops".
void f5()
{
// Make it easy to switch containers
typedef std::list<int> ContainerType;
// Create a container all set to 0: 0 0 0 0 0 0 0 0 0 0
ContainerType cont1(10);
std::cout << "Container all set to 0\n";
printc(cont1);
// Fill list with the value 5: 5 5 5 5 5 5 5 5 5 5
std::fill(cont1.begin(), cont1.end(), 5);
std::cout << "\nContainer all set to 5\n";
printc(cont1);
// Fill list with values (1..10): 1 2 3 4 5 6 7 8 9 10
std::generate(cont1.begin(), cont1.end(), NextInt);
std::cout << "\nContainer set sequentially from 1 to 10\n";
printc(cont1);
// Multiply each element by 3 (incorrect): 1 2 3 4 5 6 7 8 9 10
std::for_each(cont1.begin(), cont1.end(), TripleByVal);
std::cout << "\nEach element multiplied by 3 (incorrect)\n";
printc(cont1);
// Multiply each element by 3: 3 6 9 12 15 18 21 24 27 30
std::for_each(cont1.begin(), cont1.end(), TripleByRef);
std::cout << "\nEach element multiplied by 3\n";
printc(cont1);
// Multiply each element by 3: 9 18 27 36 45 54 63 72 81 90
std::transform(cont1.begin(), cont1.end(), cont1.begin(), TripleByVal);
std::cout << "\nEach element multiplied by 3 (again)\n";
printc(cont1);
// Create another list (same size as first list)
ContainerType cont2(cont1.size());
// Count number of even elements: 5
int count = std::count_if(cont1.begin(), cont1.end(), IsEven);
std::cout << "\nNumber of even elements: " << count << std::endl;
// Copy values from list1 to list2 where element not divisible by 6
// 9 27 45 63 81 0 0 0 0 0
std::remove_copy_if(cont1.begin(), cont1.end(), cont2.begin(), DivBy6);
std::cout << "\nCopy values from list1 to list2 where element not divisible by 6\n";
printc(cont2);
// Copy values from list1 to list2 where element not divisible by 6
// and trim the list
// List1: 9 18 27 36 45 54 63 72 81 90
// List2: 9 27 45 63 81
std::cout << "\nCopy values from list1 to list2 where element not divisible by 6 ";
std::cout << "and trim the list\n";
cont2.erase(std::remove_copy_if(cont1.begin(), cont1.end(), cont2.begin(), DivBy6),
cont2.end());
std::cout << "List1: ";
printc(cont1);
std::cout << "List2: ";
printc(cont2);
}
Full output:
In case you need a refresher, here are the details regarding the algorithms used in the example:Container all set to 0 0 0 0 0 0 0 0 0 0 0 Container all set to 5 5 5 5 5 5 5 5 5 5 5 Container set sequentially from 1 to 10 1 2 3 4 5 6 7 8 9 10 Each element multiplied by 3 (incorrect) 1 2 3 4 5 6 7 8 9 10 Each element multiplied by 3 3 6 9 12 15 18 21 24 27 30 Each element multiplied by 3 (again) 9 18 27 36 45 54 63 72 81 90 Number of even elements: 5 Copy values from list1 to list2 where element not divisible by 6 9 27 45 63 81 0 0 0 0 0 Copy values from list1 to list2 where element not divisible by 6 and trim the list List1: 9 18 27 36 45 54 63 72 81 90 List2: 9 27 45 63 81
Declarations
Implementations
The same code using lambda expressions instead of the functions:
void f3()
{
// Make it easy to switch containers
typedef std::list<int> ContainerType;
// Create a container all set to 0: 0 0 0 0 0 0 0 0 0 0
ContainerType cont1(10);
std::cout << "Container all set to 0\n";
printc(cont1);
// Fill list with the value 5: 5 5 5 5 5 5 5 5 5 5
std::fill(cont1.begin(), cont1.end(), 5);
std::cout << "\nContainer all set to 5\n";
printc(cont1);
// Fill list with values (1..10): 1 2 3 4 5 6 7 8 9 10
std::generate(cont1.begin(), cont1.end(), []{static int i = 0; return ++i;});
std::cout << "\nContainer set sequentially from 1 to 10\n";
printc(cont1);
// Multiply each element by 3 (incorrect): 1 2 3 4 5 6 7 8 9 10
auto TBV = [](int v){return v * 3;};
std::for_each(cont1.begin(), cont1.end(), TBV);
std::cout << "\nEach element multiplied by 3 (incorrect)\n";
printc(cont1);
// Multiply each element by 3: 3 6 9 12 15 18 21 24 27 30
std::for_each(cont1.begin(), cont1.end(), [](int& v){v *= 3;});
std::cout << "\nEach element multiplied by 3\n";
printc(cont1);
// Multiply each element by 3: 9 18 27 36 45 54 63 72 81 90
std::transform(cont1.begin(), cont1.end(), cont1.begin(), TBV);
std::cout << "\nEach element multiplied by 3 (again)\n";
printc(cont1);
// Create another list (same size as first list)
ContainerType cont2(cont1.size());
// Count number of even elements: 5
int count = std::count_if(cont1.begin(), cont1.end(), [](int v){return !(v %2);});
std::cout << "\nNumber of even elements: " << count << std::endl;
// Copy values from list1 to list2 where element not divisible by 6
// 9 27 45 63 81 0 0 0 0 0
auto DB6 = [](int v){return !(v % 6);};
std::remove_copy_if(cont1.begin(), cont1.end(), cont2.begin(), DB6);
std::cout << "\nCopy values from list1 to list2 where element not divisible by 6\n";
printc(cont2);
// Copy values from list1 to list2 where element not divisible by 6
// and trim the list
// List1: 9 18 27 36 45 54 63 72 81 90
// List2: 9 27 45 63 81
std::cout << "\nCopy values from list1 to list2 where element not divisible by 6 ";
std::cout << "and trim the list\n";
cont2.erase(std::remove_copy_if(cont1.begin(), cont1.end(), cont2.begin(), DB6),
cont2.end());
std::cout << "List1: ";
printc(cont1);
std::cout << "List2: ";
printc(cont2);
}
The only things that need explaining at this point are the auto variables
TBV (shorthand for TripleByValue) and DB6 (shorthand for DivideBy6).
Yes, you can name your lambda expressions if you want, and this is one of the ways. I named them because I wanted to re-use them instead of typing the entire lambda expressions again. That's all it is.
Revisiting the first example we can see how we might name our lambda expressions.
The original, anonymous way:
Declaring a variable of the correct type using std::function (harder):sort(animals.begin(), animals.end(), [](const string& l, const string& r){return l.size() < r.size();} );
Declaring a variable of the correct type using auto (easier):function<bool(const string&, const string&)> comp1 = [](const string& l, const string& r){return l.size() < r.size();}; sort(animals.begin(), animals.end(), comp1);
This is where the auto keyword really shines. It's not so much about saving keystrokes here. You can imagine that complicated functions can be challenging to get the exact type correct. Compilers are not challenged and can figure this out in their sleep.auto comp2 = [](const string& l, const string& r){return l.size() < r.size();}; sort(animals.begin(), animals.end(), comp2);
Lambda Expressions (Details)
Up to this point, all of our lambda expressions (from here on simply called lambdas) have been self-contained, meaning, the only data that was accessed were the parameters that were passed in. Let's take a step back and look at the canonical C++ program Lambda-style:Some points to make about this trivial lambda:int main() { []{cout << "Hello, World!" << endl;}(); }
But, can lambdas access any of the symbols that are local to the function (the local environment) that contains the lambda? The answer, of course, is yes.
Suppose we have a string that is local to the function and we try to access it:
We are immediately met with this error message (the name of the source file is first.cpp):void f4() { string s("Hello"); // Define the lambda expression auto lambda1 = []{cout << s << endl;}; // Call it lambda1(); }
Believe it or not, the message tells you exactly what the problem is: The variable s is not captured. Duh.first.cpp: In lambda function: first.cpp:361:28: error: 's' is not captured auto lambda1 = []{cout << s << endl;}; ^
If you want to access a non-static local symbol, you need to capture it:
Notice the [s] in the brackets. This is how you capture (gain access to) non-static local symbols. Now, the lambda will output the string as expected.void f5() { string s("Hello"); // Define the lambda expression auto lambda1 = [s]{cout << s << endl;}; // Call it lambda1(); }
Notice the phrase non-static local in the sentences above.
Output:int a = 1; // global scope static int b = 2; // file scope namespace foo {int c = 3;} // namespace scope void f9() { static int d = 4; // local static auto lambda1 = []{cout << a + b + foo::c + d << endl;}; lambda1(); }
10
Ok, so now we know how to access (non-static) local variables. Going back to the previous example, suppose that the lambda wants to modify the string like this:
We expect that the string will be changed from "Hello" to "Cello" but we are met with this error message:void f6() { string s("Hello"); auto lambda1 = [s]{s[0] = 'C'; cout << s << endl;}; lambda1(); }
Long story short, the symbol was captured by value. This is why the error message said you were trying to change a read-only location. By default, symbols are captured by value and they can't be change. The solution? Capture the symbol by reference:first.cpp: In lambda function: first.cpp:382:26: error: assignment of read-only location 's.std::basic_string<_CharT, _Traits, _Alloc>::operator[], std::allocator >(0ul)' auto lambda1 = [s]{s[0] = 'C'; cout << s << endl;}; ^
Notice the [&s] in the brackets. This is how you capture local symbols by reference. Now, the lambda can modify the string as expected:void f7() { string s("Hello"); auto lambda1 = [&s]{s[0] = 'C'; cout << s << endl;}; lambda1(); }
Output:
To see it in context:Cello
Output:void f8() { string s("Hello"); cout << s << endl; auto lambda1 = [&s]{s[0] = 'C';}; lambda1(); cout << s << endl; }
Hello Cello
We've seen that, before lambdas came along, we were using functions and function objects to provide the same kinds of behavior that the lambdas provide. How the compiler deals with lambdas isn't really that mysterious. Essentially, the lambda expression gives the compiler enough information for it to create a class with an overloaded function call operator.
Also, realize that the Standard does not specify how a compiler implements lambdas. This is just a high-level view of one possible way a compiler might implement the behavior.
Suppose we have a vector of numbers and we want to find out how many are between the numbers 3 and 7:
The compiler will create a closure class (with some internal compiler-generated name), and instantiate it where the lambda was:void f17() { std::vector<int> v {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int low = 3, high = 7; // prints the value 5 count = std::count_if(v.begin(), v.end(), [low, high](int x){return (x >= low && x <= high);}); cout << "Count is " << count << endl; }
The class name _xyz_INT_001 above was something I made up. The compiler will generate a unique name for each closure class it creates. Had I captured the locals by reference:void f17() { std::vector<int> v {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int low = 3, high = 7; class _xyz_INT_001 { public: _xyz_INT_001(int a, int b) : a_(a), b_(b) {}; bool operator()(int x) const {return (x >= a_ && x <= b_);} private: int a_; int b_; }; // prints the value 5 count = std::count_if(v.begin(), v.end(), _xyz_INT_001(low, high)); cout << "Count is " << count << endl; }
The compiler would create a closure class something like this:count = std::count_if(v.begin(), v.end(), [&low, &high](int x){return (x >= low && x <= high);});
This is what allows the captured references to be modified within the const member function, if we needed that ability.class _xyz_INT_002 { public: _xyz_INT_002(int& a, int& b) : a_(a), b_(b) {}; bool operator()(int x) const {return (x >= a_ && x <= b_);} private: int& a_; int& b_; };
The reason that the "empty" lambda is 1 byte is to be compliant with the C++ Standard. Essentially, two distinct objects must have distinct addresses. With a size of at least 1 byte, two objects will have unique addresses. This is also why using new to allocate memory and providing a size of 0 usually allocates 1 byte. Compilers can specify any size but 0, and they generally specify 1 byte. Empty base classes (i.e. interfaces) can optimize away the 1 byte. Want to know more? Google is your buddy.void f34() { int a = 5, b = 4, c = 3, d = 2; auto lambda1 = [a]{cout << a << endl;}; auto lambda2 = [&a]{cout << a << endl;}; auto lambda3 = [a, b]{cout << a << endl;}; auto lambda4 = [=]{cout << a << endl;}; auto lambda5 = [=]{cout << (a + b + c + d) << endl;}; auto lambda6 = [&a, &b, &c]{cout << a << endl;}; auto lambda7 = [&]{cout << a << endl;}; auto lambda8 = [&]{cout << (a + b + c + d) << endl;}; auto lambda9 = []{cout << "Hello!" << endl;}; // Output when compiled as a 64-bit program cout << sizeof(lambda1) << endl; // 4 cout << sizeof(lambda2) << endl; // 8 cout << sizeof(lambda3) << endl; // 8 cout << sizeof(lambda4) << endl; // 4 cout << sizeof(lambda5) << endl; // 16 cout << sizeof(lambda6) << endl; // 24 cout << sizeof(lambda7) << endl; // 8 cout << sizeof(lambda8) << endl; // 32 cout << sizeof(lambda9) << endl; // 1 (compiler dependent) }
More on Capturing
There are a variety of ways that lambdas can be introduced, giving the programmer fine-grained control over how each symbol is captured.
int a = 1, b = 2, c = 3; // Assume these are locals auto lambda1 = [a, &c]{/* ... */}; // Capture a by value and c by reference (b is not captured) auto lambda2 = [a, &c, b]{/* ... */}; // Capture a by value, c by reference, and b by value
int a = 1, b = 2, c = 3; // Assume these are locals auto lambda1 = [=, &c]{/* ... */}; // Capture a and b by value, c by reference
int a = 1, b = 2, c = 3; // Assume these are locals auto lambda1 = [&, a]{/* ... */}; // Capture b and c by reference, a by value
Deciding whether to capture by value or reference is much like how we decide if a function parameter is passed by value or reference. If you need to modify (write) the object, you must pass by reference. We also pass larger objects by reference for performance reasons.
Recall this previous example:
We have a lambda expression (right side) being assigned (stored) in a variable (left side) named comp2. There is a special name that is given to variables of this kind: We call it a closure. A closure includes not only the code, but the captured environment. In the example above, nothing is captured, so this closure is safe to use anywhere at anytime.auto comp2 = [](const string& l, const string& r){return l.size() < r.size();}; sort(animals.begin(), animals.end(), comp2);
The fundamental problem when capturing by reference is that the captured environment may no longer exist when the closure is invoked. And, since we can store a closure for use at a later time and place, it's quite possible that the captured objects are no longer valid.
Example 1, capture by value:
As you can see, there is no problem when capturing by value. However:std::function<int(void)> func0() { int a = 1, b = 2, c = 3; auto lambda1 = [a, c]{return a + c;}; return lambda1; } void f13() { auto x = func0(); int i = x(); cout << i << endl; // prints 4 }
Example 2, capture by reference:
Of course, the result is the same that would occur if you returned a pointer or reference to a local variable in a regular function. The local variables, a, b, and c, are no longer valid after func1 returns. But, we stored the closure (and references to the locals) in a variable and used it afterwards.std::function<int(void)> func1() { int a = 1, b = 2, c = 3; auto lambda1 = [&a, &c]{return a + c;}; return lambda1; } void f13() { auto x = func1(); int i = x(); cout << i << endl; // prints random values (undefined) }
In the examples above, I use several temporary variables. I often do this so as not to make the code so terse and cryptic for first-time learners. The above example can be shortened to this:
A note about this syntax:std::function<int(void)> func4() { int a = 1, b = 2, c = 3; return [&a, &c]{return a + c;}; } void f14() { cout << func4()() << endl; // prints random values (undefined) }
This syntax was introduced in C++11. The return type is the exact type of the lambda. There is also this syntax (called the trailing return type) which is also available in C++11:std::function<int(void)> func()
And C++14 fixes things so we can just use the auto keyword alone:auto func() -> std::function<int(void)>
and the compiler will deduce the return type based on what is actually being returned. If nothing is returned, the type is void. So, the example above becomes this:auto func()
And that is why auto is so nice to have. Let the compiler figure things out, if it can. This is not your parents' C++ language!auto func5() { int a = 1, b = 2, c = 3; return [&a, &c]{return a + c;}; } void f15() { cout << func5()() << endl; // prints random values (undefined) }
Deducing a Lambda's Return Type
Up until now, we haven't explicitly specified the return types from the lambdas. We just let the compiler deduce the correct type. Unfortunately, C++11 is very limited in this regard. C++11 can only deduce the type if the body is a single line return statement:
All of the examples thus far have had lambdas with trivial bodies. This code is problematic in C++11:[](const string& l, const string& r){return l.size() < r.size();}
The above will fail to compile in C++11, so you need to help the compiler by explicitly specifying the return type using the trailing return type syntax:auto lambda1 = [](bool x){if (x) return 1; else return 2;};
However, C++14 relaxes that restriction and will handle it just fine. And while I'm on the subject, C++14 also supports return type deduction for all functions (regardless of their complexity), not just lambdas. So in C++ 14, lambdas can be as complex as you want them to be and the compiler will deduce the correct type.auto lambda1 = [](bool x) -> int {if (x) return 1; else return 2;}; ^^^^^^
Of course, you can't do things like this (returning two different types):
Each return specifies different, incompatible, types. You'll see something like this error:auto lambda1 = [x]{if (x) return 1; else return 2.0;};
But, you can't do that with any function (without casting), so it's really not a limitation.error: inconsistent types 'int' and 'double' deduced for lambda return type
Lambdas in Member Functions
As is expected, you can create and use lambdas within member functions of a class. But, there are a few things to note about that.Suppose we have a lambda in a member function and we want to capture a member of that class:
The above leads to these errors:class Foo { public: Foo(int a) : a_(a) {} void bar() const { auto lam = [a_]{cout << a_ << endl;}; lam(); } private: int a_; };
If you look closely at the errors, you can see what the problem is:first.cpp: In member function 'void Foo::bar() const': first.cpp:587:16: error: capture of non-variable 'Foo::a_' auto lam = [a_]{cout << a_ << endl;}; ^ first.cpp:592:7: note: 'int Foo::a_' declared here int a_; ^ first.cpp: In lambda function: first.cpp:587:28: error: 'this' was not captured for this lambda function auto lam = [a_]{cout << a_ << endl;}; ^ first.cpp:587:28: error: invalid use of non-static data member 'Foo::a_' first.cpp:592:7: note: declared here int a_; ^
You can't capture individual members; you must capture this. Change this code:error: 'this' was not captured for this lambda function
to this code:auto lam = [a_]{cout << a_ << endl;};
and the compiler will accept it. Note that you can use default capture modes to capture this:auto lam = [this]{cout << a_ << endl;};
orauto lam = [=]{cout << a_ << endl;};
will work. But, you cannot capture this by reference directly:auto lam = [&]{cout << a_ << endl;};
g++ error message:auto lam = [&this]{cout << a_ << endl;};
Clang++ error message (clearer):first.cpp: In member function 'void Foo::bar() const': first.cpp:587:17: error: expected ','' before 'this' auto lam = [&this]{cout << a_ << endl;}; ^
The this pointer is treated special, so all of these do the same thing (capture this by value):first.cpp:587:17: error: 'this' cannot be captured by reference auto lam = [&this]{cout << a_ << endl;}; ^ 1 error generated.
These constructs are ill-formed in C++11/14:[this] [&] [=] [&, this]
Now, it's important to realize that what is being captured by value is the this pointer. Only the pointer is captured by value, meaning, a copy of the pointer is what we have. This is different than capturing the entire object by value:[&this] [=, this]
With C++11/14, this is illegal. However, with C++17, it is now legal to capture the object itself by value (copy). One situation where this might be necessary is when a lambda outlives the object that it is referencing. Capturing by value (copy) means that the object will be guaranteed to still be valid during the execution of the lambda. An example would be a lambda that's passed to another thread and is executed long after the object it references has gone out of scope and been destroyed.[*this]
C++2a (or whatever the version after C++17 will be called) will now accept this syntax:
[=, this]
Generic (Polymorphic) Lambda Expressions
The ability to have generic lambdas (templates) was added in C++14. The syntax is a little different, though. Instead of using the keyword template, you use the auto keyword:Output:void f19() { std::vector<int> iv {1, 2, 3, 4, 5}; std::vector<double> dv {1.1, 2.2, 3.3, 4.4, 5.5}; // This is a generic lambda auto lambda1 = [](auto &value){return value * value;}; std::transform(iv.begin(), iv.end(), iv.begin(), lambda1); printc(iv); std::transform(dv.begin(), dv.end(), dv.begin(), lambda1); printc(dv); }
The compiler-generated closure class may look something like this:1 4 9 16 25 1.21 4.84 10.89 19.36 30.25
class _xyz_INT_003 { public: template<typename T> bool operator()(const T& x) const {return (return x * x);} };
Generalized (Init) Captures
C++14 has given more power to the capture lists. They're called generalized captures or init captures (because you must initialize them). Here are some examples.
// "Normal" capture by value void f20() { int level = 3; auto lambda1 = [level]{cout << level << endl;}; lambda1(); }
The two examples above use the "normal" capture syntax introduced in C++11. The next two examples show how you use the init capture.// "Normal" capture by value void f21() { int level = getLevel(); auto lambda1 = [level]{cout << level << endl;}; lambda1(); }
The symbol level above is not a local symbol in the function. It's scope is only within the lambda. You don't have to declare its type like a regular symbol because the compiler will automatically deduce the type based on the initializer.// Init capture void f22() { auto lambda1 = [level = getLevel()]{cout << level << endl;}; lambda1(); }
The example above shows how you can "rename" a captured local. The symbol on the left side of the assignment operator is local to the lambda. In contrast, the symbol on the right side of the assignment operator is local to the function f23. When renaming the captured local, you can capture it as a reference:// Init capture gives new name to symbol void f23() { int level = getLevel(); auto lambda1 = [lev = level]{cout << lev << endl;}; lambda1(); }
One of the main reasons for this new functionality is because some objects can't be copied. It's not very common, but there are some. Also, capturing by reference can be problematic when storing the closure for later use, as we've seen earlier. The solution is to not make a copy of the object, but to move the object into the capture.int level = getLevel(); auto lambda1 = [&lev = level]{cout << lev << endl;};
Capturing by value with a non-copyable type produces an error:
Error from g++:void f24() { std::unique_ptr<int> p = std::make_unique<int>(8); auto lambda1 = [p]{return *p;}; cout << lambda1() << endl; }
Error from Clang++:first.cpp: In function 'void f24()': first.cpp:661:17: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>>) [with _Tp = int; _Dp = std::default_delete<int>]' auto lambda1 = [p]{return *p;}; ^ In file included from /usr/include/c++/5.1/memory:81:0, from first.cpp:8: /usr/include/c++/5.1/bits/unique_ptr.h:356:7: note: declared here unique_ptr(const unique_ptr&) = delete; ^
So, when capturing non-copyable but moveable types, this is a common use:first.cpp:661:18: error: call to deleted constructor of 'std::unique_ptr<int>' auto lambda1 = [p]{return *p;}; ^ /usr/include/c++/5.1/bits/unique_ptr.h:356:7: note: function has been explicitly marked deleted here unique_ptr(const unique_ptr&) = delete; ^ 1 error generated.
void f25() { std::unique_ptr<int> p = std::make_unique<int>(8); auto lambda1 = [myp = std::move(p)]{return *myp;}; cout << lambda1() << endl; }
The details regarding std::unique_ptr, std::make_unique, and std::move are well beyond the scope of this introduction to lambda expressions.
Finally, with generalized captures, we can capture objects that were illegal to capture in C++11. Specifically, we can capture globals. Note these various ways of capturing a global:
Output:int a = 1; // global scope void f9a() { // a is implicitly captured by reference auto lambda1 = []{cout << a << endl;}; a = 5; // change the global a lambda1(); }
However, if we use generalized capture, we can do this and capture by value:5
Output:int a = 1; // global scope void f9b() { // a is captured by value auto lambda1 = [a=a]{cout << a << endl;}; a = 5; // change the global a, doesn't affect the capture lambda1(); }
Do we really need this? I'm not 100% sure, I'm just showing how it works. I believe the motivation for this was1
Output:int a = 1; // global scope void f9c() { // a is captured by value as g auto lambda1 = [g=a]{cout << g << endl;}; a = 5; // change the global a, doesn't affect the capture lambda1(); }
Capture by reference:1
Output:int a = 1; // global scope void f9a() { // a is captured by reference as g auto lambda1 = [&g=a]{cout << g << endl;}; a = 5; // change the global a, now affects the capture lambda1(); }
Note that capturing with either these5
does not affect the way globals are captured.[=] [&]
Function Pointers and Miscellany
If you have a lambda expression that does not capture anything, you can assign it directly to a function pointer as such:This can be convenient, especially when interfacing with C code that expects maybe a function pointer as a callback function.void f27() { double (*pf)(double) = [](double d){return sqrt(d);}; // Prints 1.41421 cout << pf(2.0) << endl; double (*pfuns[])(double) = { [](double d){return sin(d);}, [](double d){return cos(d);}, [](double d){return tan(d);} }; // Prints 0.382673 0.923884 0.4142 for (auto f : pfuns) cout << f(3.1415 / 8) << " "; cout << endl; }
We don't exactly mark the variable as mutable, we mark the lambda as mutable. Notice the empty parentheses after the lambda introducer. These are necessary when providing the mutable keyword, even though no parameters are expected.
Output:void f31() { int a = 5; cout << a << endl; auto lambda1 = [a]() mutable {a++; cout << a << endl;}; lambda1(); }
Without the mutable keyword, we would see an error along these lines:5 6
first.cpp:746:26: error: cannot assign to a variable captured by copy in a non-mutable lambda auto lambda1 = [a]() {a++; cout << a << endl;}; ~^ 1 error generated.
Output:void f26() { int i = 1, j = 2; auto lambda1 = [i1 = i, j1 = j]{ int i2 = 3, j2 = 4; auto lambda2 = [=]{ cout << i1 << ", " << j1 << endl; cout << i2 << ", " << j2 << endl; }; lambda2(); }; lambda1(); }
1, 2 3, 4
Call it like this:int fib(int value) { if (value == 0 || value == 1) return value; else return fib(value - 1) + fib(value - 2); }
As a lambda expression:// Prints 55 cout << fib(10) << endl;
Notes:void f29() { std::function<int(int)> fib = [&fib](int value) { if (value == 0 || value == 1) return value; else return fib(value - 1) + fib(value - 2); }; // Prints 55 cout << fib(10) << endl; }
Lambdas and constexpr
As of C++17, lambdas are now implicitly constexpr. I've talked a little of that here. The full details can be found in this document: Generalized Constant Expressions.Before C++17, this was an error:
Error message:void f35() { auto lam = [](int value) { return value * value;}; constexpr int result = lam(10); // Error: the lambda isn't constexpr cout << result << endl; }
That's because this is how (not exactly, of course) the C++11/14 compiler generated the function:first.cpp:844:17: error: constexpr variable 'result' must be initialized by a constant expression constexpr int result = lam(10); ^ ~~~~~~~
The function is not constexpr, so can't be used to initialize result. The C++17 compiler will now generate this function:int operator()(int value) const { return value * value; }
The compiler will only mark the generated function with constexpr if it can do so. If you use constructs that can't be used with constexpr, then the function won't be marked as such. The code below is illegal because the lambda is no longer constexpr because of the use of cin:constexpr int operator()(int value) const { return value * value; }
Error message:void f36() { auto lam = [](auto value) { int a = 0; cin >> a; return value * value * a; }; constexpr int result = lam(10); // Error: the lambda isn't constexpr cout << result << endl; }
With C++17, you can now explicitly mark lambdas as constexpr and have the compiler enforce the use of it:first.cpp:857:17: error: constexpr variable 'result' must be initialized by a constant expression constexpr int result = lam(10); ^ ~~~~~~~ first.cpp:857:26: note: non-constexpr function 'operator()' cannot be used in a constant expression constexpr int result = lam(10); ^
Now, we'll get these compiler errors when the lambda is defined, not when it is called:auto lam = [](int value) constexpr { int a = 0; cin >> a; return value * value * a; };
This helps the programmer detect lambdas that can't be used as a constexpr. It's always better to have the compiler detect errors sooner (lambda definition) than later (calling the lambda).first.cpp:850:14: error: constexpr function never produces a constant expression [-Winvalid-constexpr] auto lam = [](int value) constexpr ^ first.cpp:853:9: note: non-constexpr function 'operator>>' cannot be used in a constant expression cin >> a; ^
Summary
This has been a whirlwind introduction to lambda expressions in C++, so let's recap the interesting points:
References
The examples above covered the most-used features of lambda expressions. For all of the gory details about them and more, refer to the following links: