The problem
C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.
#include <algorithm>
#include <vector>
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.
In C++03 you might be tempted to write something like the following, to keep the functor local:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
however this is not allowed, f cannot be passed to a template function in C++03.
The new solution
C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
Lambda functions are just syntactic sugar for anonymous functors.
Return types
In simple cases the return type of the lambda is deduced for you, e.g.:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
"Capturing" variables
So far we've not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
epsilon -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
You can capture by both reference and value, which you can specify using & and = respectively:
[&epsilon, zeta]captures epsilon by reference and zeta by value[&]captures all variables used in the lambda by reference[=]captures all variables used in the lambda by value[&, epsilon]captures all variables used in the lambda by reference but captures epsilon by value[=, &epsilon]captures all variables used in the lambda by value but captures epsilon by reference
The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.
What is the purpose of Lambda expressions?
Lambda expressions
Videos
The problem
C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.
#include <algorithm>
#include <vector>
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.
In C++03 you might be tempted to write something like the following, to keep the functor local:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
however this is not allowed, f cannot be passed to a template function in C++03.
The new solution
C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
Lambda functions are just syntactic sugar for anonymous functors.
Return types
In simple cases the return type of the lambda is deduced for you, e.g.:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
"Capturing" variables
So far we've not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
epsilon -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
You can capture by both reference and value, which you can specify using & and = respectively:
[&epsilon, zeta]captures epsilon by reference and zeta by value[&]captures all variables used in the lambda by reference[=]captures all variables used in the lambda by value[&, epsilon]captures all variables used in the lambda by reference but captures epsilon by value[=, &epsilon]captures all variables used in the lambda by value but captures epsilon by reference
The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.
What is a lambda expression?
The C++ concept of a lambda expression originates in the lambda calculus and functional programming. A lambda is an unnamed function that is useful (in actual programming, not theory) for short snippets of code that are impossible to reuse and are not worth naming.
In C++, the minimal lambda expression looks like:
[]{} // lambda with no parameters that does nothing
[] is the capture list and {} the function body.
The full syntax for a lambda-expression, including attributes, noexcept/throw-specifications, requires-clauses, etc. is more complex.
The capture list
The capture list defines what from the outside of the lambda should be available inside the function body and how. It can be either:
- a value:
[x] - a reference
[&x] - any variable currently in scope by reference
[&] - same as 3, but by value
[=] - capturing
thisand making member functions callable within the lambda[this]
You can mix any of the above in a comma separated list [x, &y].
Init-captures (C++14)
An element of the capture list can now be initialized with =, which is called init-capture.
This allows renaming of variables and to capture by moving. An example taken from the standard:
int x = 4;
auto y = [&r = x, x = x+1]()->int {
r += 2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.
and one taken from Wikipedia showing how to capture with std::move:
auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};
The template parameters (C++20)
Since C++20, lambda expressions can have a template-parameter-list:
[]<int N>() {};
Such a generic lambda is like a non-template struct with a call operator template:
struct __lambda {
template <int N> void operator()() const {}
};
The parameter list
The parameter-declaration-clause is the same as in any other C++ function.
It can be omitted completely when there are no parameters, meaning that [](){} is equivalent to []{}.
Generic Lambdas (C++14)
Lambdas with an auto parameter are generic lambdas.
auto would be equivalent to T here if
T were a type template argument somewhere in the surrounding scope):
[](auto x, auto y) { return x + y; }
This works just like a C++20 abbreviated function template:
struct __lambda {
// C++20 equivalent
void operator()(auto x, auto y) const { return x + y; }
// pre-C++20 equivalent
template <typename T, typename U>
void operator()(T x, U y) const { return x + y; }
};
Return type (possibly deduced)
If a lambda has only one return statement, the return type can be omitted and has the implicit type of decltype(return_statement).
The return type can also be provided explicitly using trailing return type syntax:
[](int x) -> int { return x; }
Improved Return Type Deduction (C++14)
C++14 allows deduced return types for every function and does not restrict it to functions of the form return expression;. This is also extended to lambdas.
By default, the return type of a lambda is deduced as if its return type was declared auto.
Mutable lambda (C++14)
If a lambda is marked mutable (e.g. []() mutable { }) it is allowed to mutate the values that have been captured by value.
mutable means that the call operator of the lambda's type does not have a const qualifier.
The function body
A block-statement will be executed when the lambda is actually called. This becomes the body of the call operator.
Use cases
The library defined by the ISO standard benefits heavily from lambdas and raises the usability several bars as now users don't have to clutter their code with small functors in some accessible scope.
Hello, can anyone explain lambda expressions? I kNow I am using it when I set up a thread like in Thread t = new Thread(()=> FUNCTIONNAME). But I don’t understand it. Can anyone explain it maybe with an example or does anyone know some good references?
Thanks!