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.

  1. By Value
  2. 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.

// 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,

#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