Introduction
C++11 provides the ability to create anonymous functions, called Lambda Functions. Lambda Expression in C++ allows a function to be defined at the point where it’s needed in another expression. It is a function that we can write inline in our code in order to pass in to another function.
Syntax
[ capture clause ] (parameters) -> return-type { // Definition of method }[] identifier, called the capture specification. It tells the compiler we’re creating a lambda function. Next up, like any other function, is argument list: (). If compiler can deduce the return value of the lambda function, it will do it rather than force you to include it.
Capture
A lambda with empty capture clause [ ] can access only those variable which are local to it. A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope. Ways to capture external variables from enclosing scope are :
- [&] : Capture all external variable by reference
- [=] : Capture all external variable by value
- [a, &b] : Capture a by value and b by reference
- [a] : Capture variable a by value
- [&a] : Capture variable a by reference
Capture example
- Capture by value
auto upperBound = 42; [upperBound](int value) { return 0 < value && value < upperBound; }
- Capture by reference
auto upperBound = 42; [&upperBound](int value) { return 0 < value && value < upperBound; }
- Capture all by value
[=] will save non-static local variables needed in the body of the lambda by value.m_upperBound = 42; [=](int value) { // Doesn't compile, m_upperBound is not a non-static local return 0 < value && value < m_upperBound; }
- Capture all by value, and except few
With using [=, &divisor] as a capture, everything will be captured by value except for the variable that is explicitly listed preceded with an &. - Capture this
You can also save the surrounding object like this: [this]. this is a pointer to the enclosing object, so if you capture this, you’ll have access to the members.[this](int value) { return 0 < value && value < this->m_upperBound; }
Parameters
The list of parameters, as usual, come in between parentheses i.e. (). Some remarks:
- In C++11 you cannot use auto as a type-specifier. But since C++14, you may.
- If there are no parameters passed to a lambda, the empty list can be omitted. Meaning that []{} is a valid lambda expression. Though for readability reasons, it’s better not to remove the empty parenthesis.
Return type
The return type of lambda expressions can be and most often is omitted when
- it is void
- if it deducible (so if you could use auto)
If you do have to or want to declare them, you must use the [trailing return type syntax] meaning that you will declare the type between the parameter list and the body, putting the type after an arrow like this:
[](int value) -> bool { return 0 < value && value < 10; }
Body
It’s same as that of normal function body. As a best practice, it should be a quite lean one. If you need something longer, heavier, maybe a lambda is not the way you go. As a reminder let’s mention that you can work with the following variables:
- local variables declared in the body
- parameters passed into the lambda
- non-static local variable captured within the square brackets called a “capture”
Example
// Lambda expression using namespace std; int main() { vector<int> v1 = {3, 1, 7, 9}; vector<int> v2 = {10, 2, 7, 16, 9}; // Access v1 and v2 by reference auto pushinto = [&] (int m) { v1.push_back(m); v2.push_back(m); }; // Pushes 20 in both v1 and v2 pushinto(20); // Access v1 by copy [v1]() { for (auto p = v1.begin(); p != v1.end(); p++) { cout << *p << " "; } }; int N = 5; // Find first number greater than N // [N] denotes, can access only by value vector<int>:: iterator p = find_if(v1.begin(), v1.end(), [N](int i) { return i > N; }); // Count numbers greater than or equal to N // [=] denotes, can access all variable int count_N = count_if(v1.begin(), v1.end(), [=](int a) { return (a >= N); }); }