How To Use Lambda Expressions in C++11
A Basic Example
void main()
{
// Define the lambda:
auto functionName = [] () { cout << "Hello World."; };
// Call the lambda:
funcionName();
// Here's a lambda with arguments:
auto arg_lambda = [] (int x, int y) { cout << x << ", " << y; };
// Calling it:
arg_lambda(1, 2);
}
auto functionName =
Assign the lambda to a variable we can reference.[]
The lambda operator.()
Same as every function argument list. Optional if empty.{ ... }
The body of the lambda.
C++ is back to its old bad habits of overloading its operators with drastically different meanings that both you and the compiler must infer from the context.
The operator []
we all know from our old friend, the array definition. This time it has been re-purposed to define the lambda.
They’ve done this to us before. Anybody who remembers their first time with pointers in C, and trying to decipher if *my_variable
was going to define a pointer, or deference to some other type will now be suffering flashbacks.
The thing to look for here is that the []
operator for lambdas will not follow a variable name.
my_array[] = { 1, 2, 3 }; // defines an array
my_lambda = [] { return my_array; }; // defines a lambda
You can think of []
as the name of a function, since it follows pretty much the same format:
auto myFunction() -> void { ... };
[] () -> void { ... };
Lambdas work like an expression
template<typename MyFunctionType>
void printStuff(MyFunctionType my_function)
{
// The lambda is passed in as an argument and actually called here.
my_function();
}
void main()
{
// Define the lambda here
auto functionName = [] { cout << "Hello World."; };
// Pass it to the method here
printStuff(functionName);
// Or just define it on the spot like a slob:
printStuff( [] { cout << "Hello on the fly."; } );
}
Variable Capture
Lambdas can cheat on scope and use variables defined outside.
void main()
{
string name;
cin >> name;
auto func = [&] { cout << "Hello " << name; };
func();
}
[&]
Defines a lambda with variable capture ability. Specifically capture by reference. (The value can be modified outside the lambda!)[=]
Will copy the variable to the lambda’s scope for use.[=, &x]
With this notation, you can copy all captured variables for local use, but define the variable namedx
to be captured by reference. Replacex
with your specific variable name.[x]
Capturesx
only as a copy.[this]
Captures thethis
pointer of the class that calls it.
Return Values
Lambdas can be given return values like functions:
// compiler guess you are returning an int.
[] () { return 1; };
Return types might need to be made more clear:
// programmer is explicitly defining the return type:
[] () -> int {return 1;};
-> int
Defines the return type as int.
This is a new C++ 11 way of defining return types.
// this
int myFunction();
// becomes this
auto myFunction() -> int;
So they are doing it to us again with the overloaded arrow operator. I think that keeping a certain level of confusion in the language is meant as an attempt to sort the new programmers from the greybeards.
MyDynamicObject -> myMethod();
// is completely different than this use of ->
[] () -> std::string { ... };
Lambdas as Arguments and Return Types
Use std::function
as the type when you want to collect a lambda in the argument list or return one from your “lambda factory” method.
This requires the #include <functional>
header to use.
So you could skip the template and do this:
template<typename MyFunctionType>
void printStuff(MyFunctionType my_function)
{
// The lambda is passed in as an argument and actually called here.
my_function();
}
// becomes this:
void printStuff(std::function<void()> my_function)
{
my_function();
}
Or if an argument is required for the lambda:
void myFunction(std::function<void (string)> my_lambda)
{
string my_string = "Hello World.";
my_lambda(my_string);
}
// call it
myFunction([] (string x) { cout << "my string x=" << x; });
// prints:
// my string x=Hello World.
Or the lambda will have a return value:
void myFunction(std::function<bool (string)> my_lambda)
{
string my_string = "Hello";
bool is_printable = my_lambda(my_string);
}
// call it
myFunction( [] (string x) -> bool {
if (isPrintable(x)) return true;
else return false;
});
Or the lambda will have multiple arguments:
void myFunction(std::function<string (string, int)> my_lambda)
{
string my_string = "Hello";
int my_int = 1;
cout << my_lambda(my_string, my_int);
}
Or as a return value:
std::function createFunctions()
{
return [] () -> int { return 42; };
}
And you can see if std::function has been assigned a function by treating it like a bool:
std::function<int ()> myFunciton;
if (myFunction) {
// will not execute
myFunciton();
}
myFunction = [](){cout << "Hello World.";};
if (myFunction) {
// will print "Hello World."
myFunction();
}
Why?
Why use lambdas?
- Easier to implement than creating a delegate to pass one or two functions as parameters.
- Look at JavaScript and JQuery and see all the uses for closures. It’s the same thing. Lambdas make for great callback arguments.
-
Use with iterators in a for_each method:
vector
my_vector; for_each(my_vector.begin(), my_vector.end(), [] (string list_item) { cout « list_item « endl; });
- Pass an action to a function without wrapping it up in a class.
My Opinion
I hate nesting. Lambdas that get defined as in-line functions are going to add another nested layer to your code.
In my code, I like to keep things clean. I don’t want to go searching for that opening brace. Especially in loops. I tend to nest a try/catch block immediately within loops so any exceptions don’t break them. That already adds too many nested blocks.
You will more often than not see my code called like this:
myFunction (std::vector<std::string> string_list) {
for_each(string_list.begin(), string_list.end(), [] (std::string list_item) {
eachListItem(list_item);
});
}
The eachListItem()
will contain the try/catch block and any logic. This reduces the nesting and improves readability. This is a pattern I follow in JavaScript all the time, where my closures just call some other function to do the work.
Inline functions are neat, and they have their uses. But I believe that a function should do one thing, and one thing only. When we start defining functions in-line like this it can get out of hand real fast.