Lambda Expression in C++11 and how to use

In this post, we are going to learn Lambda Expression in C++11 and how to use program examples.

What are Lambda expressions


In modern C++ (11, 14, 17 & 20), a lambda expression—also called a lambda—is a shorthand to defining a function object directly in your code where it is invoked. Lambdas are used to encapsulate a few lines of code. Let us understand more details with an example:

Syntax

[=] () mutable throw () -> int {}   // sample 1
[=] () mutable nothrow () -> int {} // sample 2

Parameters

  • [=] – Capture Clause
  • () Parameter list
  • throw() – exception specification
  • int – Trailing return type
  • {} – Lambda body

As you can see the two sample declarations above, now let us understand the syntax and its parameters in detail:

1. [=]–Capture Clause


[=] – capture clause – lambda functions can access or capture the variable from the surrounding scope. [] is the starting of a lambda function which describes the variables which a lambda function is capturing from surrounding to operate on. The variables can be captured in different ways like by value or by reference.

Let us understand with examples below:

Program Example

[] - captures no variables.

[=] - capture local objects (local variables, parameters) in scope by value.

[&] - capture local objects (local variables, parameters) in scope by reference.

[this] - capture this by reference.

[var, &var1] - capture objects var by value, var1 by reference.

[=,&var] - capture local objects (local variables, parameters) in scope by value, but capture var by reference.

important note

Some of the important points to note here are the mixing of capture mode that can lead to errors. Let us understand some examples below:

Program Example

[&, var]{};      // This is allowed, capture all by reference and var by value

 [&, &var]{};     // ERROR: capture all by reference and then no need to specify single local variable again as a reference. var is in the local scope.

[=, this]{};   // ERROR: when = is the default capture then capturing ‘this’ by reference will cause an error.

 [=, *this]{ }; // OK: But if you captures ‘this’ by value with = as default this is OK.

[var, var]{};      // ERROR: cannot capture same variable twice

2. () – Parameter list

Along with capturing variables, a lambda expression can also accept parameters like a normal function in C++. So if you have some arguments to pass then you can do so, if you don’t have any parameters then you can leave it blank.

[] (int var, int var1)
{
   return var*var1;
}

In C++14, you can pass arguments using auto that can help you write generic code so above example will become like:

[] (auto var, auto var1)
{
   return var*var1;
}

You can pass a lambda expression as an argument to another lambda expression.

Since a parameter list is optional, you can skip the empty parentheses if you do not have an argument to the lambda expression and you do not have a mutable, exception specifier and trailing return type to follow.

mutable – mutable specification.

A lambda’s function is const-by-value, it does not allow to change the values of variables that are captured by value the mutable keyword allows the body of a lambda expression to modify variables that are captured by value.

3. throw() – exception specification


When defining the lambda function. We can mention the noexcept exception specification to indicate that the lambda expression does not throw any exceptions. Also, we have except exception specifier that can be used.

4.-> int – Trailing return type


The return type of a lambda expression is automatically deduced by the compiler. If we want to use a specific return type with lambda we can do so by providing a trailing return type. It is totally optional and as per your need.

Once you specify the trailing return type then you use an auto with it.

5. {} – Lambda body


At the end we have the body of the lambda expression where you can add your logic that you want in this function.

important points to remember working with lambda in C++ 11/14/17/20


There are some of the important points to remember when working with lambda in C++ 11/14/17/20:

  • When we capture by Reference, a variable can be modified outside by some other thread or function, but when capture by value then its value cannot be modified. To be able to change variables captured by the value we must use a mutable keyword.
  • When we capture by Reference, it reflects updates done to variables in scope outside of our lambda, but value captures do not let the changed values get reflected outside.
  • When using Reference captures, we must make sure the variable lifetime is available when lambda try to access it, if it is already deleted by some other function or thread then lambda will result in failure with an access violation.
  • When we do value captures then it has no lifetime dependencies. It will be guaranteed available to lambda at the time of execution.