A lambda expression provides a concise way to create simple function objects. A lambda expression is a prvalue whose result object is called closure object, which behaves like a function object.
The name 'lambda expression' originates from lambda calculus, which is a mathematical formalism invented in the 1930s by Alonzo Church to investigate questions about logic and computability. Lambda calculus formed the basis of LISP, a functional programming language. Compared to lambda calculus and LISP, C++ lambda expressions share the properties of being unnamed, and to capture variables from the surrounding context, but they lack the ability to operate on and return functions.
A lambda expression is often used as an argument to functions that take a callable object. That can be simpler than creating a named function, which would be only used when passed as the argument. In such cases, lambda expressions are generally preferred because they allow defining the function objects inline.
A lambda consists typically of three parts: a capture list []
, an optional parameter list ()
and a body {}
, all of which can be empty:
[](){} // An empty lambda, which does and returns nothing
Capture list
[]
is the capture list. By default, variables of the enclosing scope cannot be accessed by a lambda. Capturing a variable makes it accessible inside the lambda, either as a copy or as a reference. Captured variables become a part of the lambda; in contrast to function arguments, they do not have to be passed when calling the lambda.
int a = 0; // Define an integer variable
auto f = []() { return a*9; }; // Error: 'a' cannot be accessed
auto f = [a]() { return a*9; }; // OK, 'a' is "captured" by value
auto f = [&a]() { return a++; }; // OK, 'a' is "captured" by reference
// Note: It is the responsibility of the programmer
// to ensure that a is not destroyed before the
// lambda is called.
auto b = f(); // Call the lambda function. a is taken from the capture list and not passed here.
Parameter list
()
is the parameter list, which is almost the same as in regular functions. If the lambda takes no arguments, these parentheses can be omitted (except if you need to declare the lambda mutable
). These two lambdas are equivalent:
auto call_foo = [x](){ x.foo(); };
auto call_foo2 = [x]{ x.foo(); };
The parameter list can use the placeholder type auto
instead of actual types. By doing so, this argument behaves like a template parameter of a function template. Following lambdas are equivalent when you want to sort a vector in generic code:
auto sort_cpp11 = [](std::vector<T>::const_reference lhs, std::vector<T>::const_reference rhs) { return lhs < rhs; };
auto sort_cpp14 = [](const auto &lhs, const auto &rhs) { return lhs < rhs; };
Function body
{}
is the body, which is the same as in regular functions.
Calling a lambda
A lambda expression's result object is a closure, which can be called using the operator()
(as with other function objects):
int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; };
std::out << timesFive(2); // Prints 10
multiplier = 15;
std::out << timesFive(2); // Still prints 2*5 == 10
Return Type
By default, the return type of a lambda expression is deduced.
[](){ return true; };
In this case the return type is bool
.
You can also manually specify the return type using the following syntax:
[]() -> bool { return true; };
Mutable Lambda
Objects captured by value in the lambda are by default immutable. This is because the operator()
of the generated closure object is const
by default.
auto func = [c = 0](){++c; std::cout << c;}; // fails to compile because ++c
// tries to mutate the state of
// the lambda.
Modifying can be allowed by using the keyword mutable
, which make the closer object's operator()
non-const
:
auto func = [c = 0]() mutable {++c; std::cout << c;};
If used together with the return type, mutable
comes before it.
auto func = [c = 0]() mutable -> int {++c; std::cout << c; return c;};
An example to illustrate the usefulness of lambdas
Before C++11:
// Generic functor used for comparison
struct islessthan
{
islessthan(int threshold) : _threshold(threshold) {}
bool operator()(int value) const
{
return value < _threshold;
}
private:
int _threshold;
};
// Declare a vector
const int arr[] = { 1, 2, 3, 4, 5 };
std::vector<int> vec(arr, arr+5);
// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(), islessthan(threshold));
Since C++11:
// Declare a vector
std::vector<int> vec{ 1, 2, 3, 4, 5 };
// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
auto it = std::find_if(vec.begin(), vec.end(), [threshold](int value) { return value < threshold; });