Lambda in C++ 11

Share the Article

Lambda in C++ consist of expressions and statements (popularly called Lambda expressions). These expressions cause compilers to generate internal classes. Such internal classes are called Closure Class. Finally, from the closure class, C++ compiler instantiate objects during runtime. These runtime-objects are similar to ordinary objects but they encapsulate the lambda functionality. The lambda expressions, become part of source code of the closure classes.

The terms, lambda, closure, closure-classes generally are used interchangeably.

Example of simplest lambda is:

auto lambda = [](){};

Other example:

int x, y; auto c1 = [x](int y) { return x * y > 55; };

In the above statement, the statement on the right-hand side is Lambda expression. Finally, c1 is the runtime object. The program will use this c1 to get the functionality defined by lambda.

The lambda in C++ contains 2 capture modes, by-reference and by-value capture mode.

By-Reference Capture Mode

By-reference capture mode will cause a closure class to create the member variables of reference type. These references point to local variables which are present in the same scope as lambda. To enable creation of by-reference, the square brackets use ampersand [&].

The following example forms a basic lambda with by-reference mode. The variable “tmp” inside lambda becomes reference to local variable “tmp” in initialize function.

#include <iostream> //main header #include <functional> //for function ptr using namespace std; //namespace std::function<void()> fp; void initfunda() { std::string tmp = "temporary string"; cout << "Address in function = " << &tmp << endl; fp = [&](){ cout<<"Address in lambda = " <<&tmp<<endl; }; fp(); //call lambda } int main() { initfunda(); return 0; }

The output from compiler shows variable “tmp” has same address both outside and inside the class.

Lambda functions with reference capture

There is another way to enable by-reference, i.e. by explicit reference of “tmp”. The following code snippet uses explicit reference.

fp = [&tmp](){ cout<< "Address in lambda = "<<&tmp<<endl; };

By-value capture mode

By-value capture mode will cause a closure class to create copies of local variables. Therefore, the local variables which are in the same scope just copies. To enable by-value, the square brackets use equal-to sign [=].

The following example forms a basic lambda with by-value mode. The variable “tmp” inside lambda becomes copy of local variable “tmp” in initialize function.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace std::function<void()> fp; void initfunda() { std::string tmp = "temporary string"; cout << "Address in function = " << &tmp << endl; fp = [=](){ cout <<"Address in lambda = "<<&tmp<<endl; }; fp(); //call lambda } int main() { initfunda(); return 0; }

The output of program shows that both the “tmp” variables have different addresses.

Lambda function with by value capture mode

Just like explicit by-reference, there is similar format for explicit by value.

fp = [tmp](){ cout << "Address in lambda = "<<&tmp<< endl;};

Capture by-value with class objects

In case, the local variables are objects, then for doing by-value capture, compiler shall call copy-constructor to create copy of objects. The following example illustrates this.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace class MainFunda { public: MainFunda() { cout << "MainFunda()\n"; } MainFunda(const MainFunda&) { cout << "MainFunda(const MainFunda&)\n"; } ~MainFunda() { cout << "~MainFunda()\n"; } }; int main() { MainFunda a1; auto fp = [a1](){ }; //Invoke copy constructor return 0; }

The output shows copy constructor called for copying object a1 in lambda closure.

Lambda function capture class object by value

Capture do not happen for static variables in local scope

The following examples shows that “tmp” is now static variable. Therefore, no copy of “tmp” happens inside closure class. The lambda expression shall be calling the actual static variable even with by-value capture mode.

#include <iostream> //main header #include <functional> //for function ptr using namespace std; //namespace std::function<void()> fp; void initfunda() { static std::string tmp = "temporary string"; cout << "Address in function = " << &tmp << endl; fp = [=](){ cout <<"Address in lambda = "<<&tmp<<endl; }; fp(); //call lambda } int main() { initfunda(); return 0; }

The output shows that address of “tmp” inside lambda is same as address of static variable “tmp”.

lambda function trying to capture static variable

When capture by-reference and reference variable goes out of scope

The capture by-reference mode should be used very carefully. This is because, there is a possibility that lifetime of lambda shall out-live the scope of local variables. In following example, “fp” is global variable so the lambda will live till the program is running. However, local variable “tmp” will be destroyed the moment initialize function returns. Hence, reference to “tmp” will become dead inside lambda. Thereafter, any use of “tmp” will have undefined behavior.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace std::function<void()> fp; void initfunda() { std::string tmp = "temporary string"; fp = [&](){ cout << "Executing Lambda : " << tmp << endl; }; fp(); //Valid Call } int main() { initfunda(); fp(); //Invalid Call return 0; }

The output shows that when main function calls lambda, then it prints garbage value for “tmp”.

by reference capture variable goes out of scope

Use static variable or capture by-copy to solve the problem

The following problem solves the problem of by-reference by making the tmp variable as static. Therefore, “tmp” will live till this program is running.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace std::function<void()> fp; void initfunda() { static std::string tmp = "temporary string"; fp = [&](){ cout <<"Executing Lambda : "<<tmp<< endl; }; fp(); //Valid Call } int main() { initfunda(); fp(); //Valid Call return 0; }

Output:

Lambda function with static in c++

Capture by-reference of non-automatic variables

Capture should happen only for local variables. When the program forcefully passes a global variable for capture, then compiler shall give error.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace std::function<void()> fp; std::string tmp = "temporary string"; //global void initfunda() { fp = [&tmp](){ cout<<"Executing Lambda : "<<tmp<<endl; }; fp(); //call lambda } int main() { initfunda(); fp(); //call lambda return 0; }

However, Some newer compiler may generate warning instead of error.

compiler error for lambda capturing global variable in c++

Same rule applies on by-value capture.

The following code snippet uses tmp to enable capture by-value.

fp = [tmp](){ cout << "Executing Lambda : "<< tmp << endl; };

Output

compiler error for lambda value capture with global variable

Move object inside Lambda

In C++14, there is better capture mode which has more flexibility. This is generalized or init capture mode. The following program uses generalized capture mode to move an object to lambda. This mode can achieve what by-reference and by-value mode cannot.

Syntax:

[<Name of Data member in closure class> = <Expression>]

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace std::function<void()> fp; std::string tmp = "temporary string"; //works for non-local too void initfunda() { fp = [tmp=std::move(tmp)]() { cout << "Executing Lambda : " << tmp << endl; }; fp(); //call lambda } int main() { cout << "tmp = " << tmp << endl; initfunda(); cout << "tmp = " << tmp << endl; fp(); return 0; }

The output shows that when program moves “tmp” inside lambda, then it becomes available only on invocation of lambda and not outside it.

move object inside lambda function in c++

Main Funda: Lambda in C++ is another way to create function objects

Related Topics:

Lambda in C++14
Lambda in C++17
What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism
emplace_back vs push_back

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *