In this article we will discuss how to capture local variables from outer scope in Lambda.
A simple Lambda syntax is,
[Captured variables](paameters) { function code }
Local variables from outer scope can be captured inside Lambda in 2 modes i.e.
- By Value
- By Reference
Capturing Local Variables by value inside Lambda Function
To capture the local variables by value, specify their name in capture list i.e.
Frequently Asked:
// Local Variables std::string msg = "Hello"; int counter = 10; // Defining Lambda function and // Capturing Local variables by Value auto func = [msg, counter] () { //... };
Now, the variables specified in capture list will be copied inside lambda by value. Inside lambda they can be accessed but can not be changed, because they are const.
To modify the we need to add mutable keyword i.e.
auto func = [msg, counter] () mutable { };
Now the captured variables can be modified. But their modification will not affect value of outer scope variables, because they are captured by value.
Checkout this example,
Best Resources to Learn C++:
#include <iostream> #include <string> int main(int argc, char **argv) { std::string msg = "Hello"; int counter = 10; // Defining Lambda function and // Capturing Local variables by Value auto func = [msg, counter] () mutable { std::cout<<"Inside Lambda :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl; // Change the counter & msg // Change will not affect the outer variable because counter variable is // captured by value in Lambda function msg = "Temp"; counter = 20; std::cout<<"Inside Lambda :: After changing :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl; }; //Call the Lambda function func(); //Values of local variables are not changed. std::cout<<"msg = "<<msg<<std::endl; std::cout<<"counter = "<<counter<<std::endl; return 0; }
Output:
Inside Lambda :: msg = Hello Inside Lambda :: counter = 10 Inside Lambda :: After changing :: msg = Temp Inside Lambda :: After changing :: counter = 20 msg = Hello counter = 10
Capturing Local Variables by Reference inside Lambda
To capture the local variables by reference, specify their name in capture list with prefix & i.e.
// Local Variables std::string msg = "Hello"; int counter = 10; // Defining Lambda function and // Capturing Local variables by Reference auto func = [&msg, &counter] () { //... };
Now, the variables specified in capture list will be captured inside lambda by Reference. Inside lambda they can be accessed and their value can also be changed. Also, their modification will affect value of outer scope variables, because they are captured by Reference.
Checkout this example,
#include <iostream> #include <string> int main(int argc, char **argv) { std::string msg = "Hello"; int counter = 10; // Defining Lambda function and // Capturing Local variables by Reference auto func = [&msg, &counter] () { std::cout<<"Inside Lambda :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl; // Change the counter & msg // Change will affect the outer variable because counter variable is // captured by Reference in Lambda function msg = "Temp"; counter = 20; std::cout<<"Inside Lambda :: After changing :: msg = "<<msg<<std::endl; std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl; }; //Call the Lambda function func(); std::cout<<"msg = "<<msg<<std::endl; std::cout<<"counter = "<<counter<<std::endl; return 0; }
Output:
Inside Lambda :: msg = Hello Inside Lambda :: counter = 10 Inside Lambda :: After changing :: msg = Temp Inside Lambda :: After changing :: counter = 20 msg = Temp counter = 20
Capture All Local Variables from outer scope by Value
To capture all local variables from outer scope by value, pass “=” in the capture list i.e.
// Capturing all Local variables by Value auto func = [=] () { //... };
Capture all local variables from outer scope by Reference
To capture all local variables from outer scope by Reference, pass “&” in the capture list i.e.
// Capturing all Local variables by Reference auto func = [&] () { //... };
Mixing capturing by value and Reference
We can also mix the capturing mode of Lambda by passing some local variables by value and some by reference i.e.
// Capturing All Local variables by value except counter, which is // captured by reference here. auto func = [=, &counter] () mutable {};
Be-aware of capturing local variables by Reference in Lambda
If in lambda we are capturing local variables by reference, then we need to make sure that when lambda function is accessed or called, then all the by reference captured local variables are still in scope.
If lambda will try to access or modify a by reference captured local variable, which is not in scope anymore i.e. which has been destroyed due to stack unwinding, then crash can happen.
Checkout this example,
#include <iostream> #include <string> #include <functional> std::function<void ()> getCallBack() { // Local Variable int counter = 10; // Defining Lambda function and // Capturing Local variables by Reference auto func = [&counter] () mutable { std::cout<<"Inside Lambda :: counter = "<<counter<<std::endl; // Change the counter // Change will affect the outer variable because counter variable is // captured by Reference in Lambda function counter = 20; std::cout<<"Inside Lambda :: After changing :: counter = "<<counter<<std::endl; }; return func; } int main(int argc, char **argv) { std::function<void ()> func = getCallBack(); //Call the Lambda function func(); // Lambda function will access and modify the variable that has been captured it by reference // But that variable was actually a local variable on stack which was removed from stack when getCallbacK() returned // So, lambda function will basically corrupt the stack return 0; }
Output
Inside Lambda :: counter = 0 Inside Lambda :: After changing :: counter = 20 Segmentation fault (core dumped)
Here we tried to accessed the variable that has already been destructed due to stack unwinding.
To compile the above examples in linux use following command,
g++ –std=c++11 example.cpp
Inside Lambda :: counter = -858993460
Inside Lambda :: After changing :: counter = 20
Press any key to continue . . .
It is not crashing, please find the output
Hi Atul,
Well its an undefined behaviour, some times it will crash, sometimes will give garbage values.
See the value of counter on your output, its garbage.
I tested on linux and it crashed. So, its undefined behaviour.
yes, it don’t crash on mac too, and i agree with Varun, the result this program is undefined.
Thank you ! This article was very helpful and concise.