Lambda Functions were introduced in C++11, and it allows us to write Inline, Anonymous Functions. Lambda Functions are also called Lambda Expressions. These Lambda expressions/functions are particularly useful when you need to pass a small function as an argument to another function as callbacks, such as a sorting algorithm or a thread constructor. In this article, we’ll look how to capture local variables within a Lambda Expression / lambda Function in C++.
Table of Contents
What Are Lambda Expressions in C++?
Before we explore variable capturing, let’s understand the basic syntax of a lambda expression:
[Captured variables](parameters) { function code }
The []
is known as the capture clause, the ()
holds any parameters the lambda accepts (like a regular function), and the {}
contains the function code that defines the body of the lambda expression.
Capturing Local Variables into a Lambda Function in C++
When you define a lambda expression within a function, you may want to use variables from its enclosing scope, also known as the outer scope. To do this, you can capture these variables in two main ways:
- By Value
- By Reference
Capturing Variables by Value into a Lambda Function in C++
Capturing by value means the lambda will have its own copy of the captured variables. These copies are made at the point where the lambda is defined, not when it’s called. Here’s how you can capture variables by value:
// Local Variables std::string msg = "Hello"; int counter = 10; // Defining Lambda function and Capturing Local variables by Value auto func = [msg, counter]() { // Accessing the captured variables };
The variables msg
and counter
are now available within the lambda, but they’re const
by default – meaning you cannot modify them. To do so, you need to declare the lambda as mutable
.
auto func = [msg, counter]() mutable { // Now we can modify msg and counter };
Here is a complete example,
Frequently Asked:
- How to Print a vector in C++?
- How to Capture Member Variables in Lambda function in C++?
- Lambda Functions in C++
- C++11 Lambda: Capture Variables by Reference or Value
#include <iostream> #include <string> int main() { std::string msg = "Hello"; int counter = 10; auto func = [msg, counter]() mutable { std::cout << "Inside Lambda :: msg = " << msg << std::endl; std::cout << "Inside Lambda :: counter = " << counter << std::endl; msg = "Temp"; counter = 20; std::cout << "Inside Lambda :: After changing :: msg = " << msg << std::endl; std::cout << "Inside Lambda :: After changing :: counter = " << counter << std::endl; }; 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 = Hello counter = 10
As you can see, changes made to msg
and counter
within the lambda do not affect the originals outside it.
Capturing Variables by Reference into a Lambda Function in C++
If you want your lambda to reflect changes to the captured variables back to the outer scope, you should capture them by reference:
// Local Variables std::string msg = "Hello"; int counter = 10; // Defining Lambda function and Capturing Local variables by Reference auto func = [&msg, &counter]() { // Inside lambda, msg and counter refer to the original variables };
A complete example of capturing by reference would look like this:
#include <iostream> #include <string> int main() { std::string msg = "Hello"; int counter = 10; auto func = [&msg, &counter]() { std::cout << "Inside Lambda :: msg = " << msg << std::endl; std::cout << "Inside Lambda :: counter = " << counter << std::endl; msg = "Temp"; counter = 20; std::cout << "Inside Lambda :: After changing :: msg = " << msg << std::endl; std::cout << "Inside Lambda :: After changing :: counter = " << counter << std::endl; }; 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
Notice how the changes within the lambda are reflected outside
it because msg
and counter
are captured by reference.
Capturing All Local Variables
You can also capture all local variables without specifying them one by one:
- By value using
[=]
- By reference using
[&]
And you can mix modes:
auto func = [=, &counter]() mutable { // All variables captured by value, except counter by reference };
Capture Variables Carefully in Lambda Function in C++
Be cautious when capturing by reference, especially if the lambda might outlive the captured variables. If a lambda captures a reference to a local variable that goes out of scope, accessing that variable later will lead to undefined behavior, potentially crashing your program.
Here’s an example that demonstrates the danger:
#include <iostream> #include <functional> std::function<void()> getCallback() { int counter = 10; auto func = [&counter]() mutable { std::cout << "Inside Lambda :: counter = " << counter << std::endl; counter = 20; }; return func; } int main() { std::function<void()> func = getCallback(); func(); // Undefined behavior: the original counter is gone return 0; }
Output
Inside Lambda :: counter = 22092 Segmentation fault (core dumped)
Here, we tried to access a variable that had already been destroyed due to stack unwinding. Basically, the lambda Expression in this example outlives counter
, leading to a crash when it tries to modify it.
Compiling Lambda Expressions in C++
To compile code with lambda expressions in C++, make sure to enable the C++11 (or later) standard:
g++ -std=c++11 example.cpp
Summary
Lambda expressions offer a concise and powerful way to define local functions that can access and manipulate variables in their enclosing scope. By understanding how to capture variables by value or by reference, you can write more expressive and functional code in C++. Always remember to manage the scope of your variables carefully to avoid undefined behavior.
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.