In C++, lambda expressions are a convenient way to create anonymous functions that can capture external variables and use them within their bodies. This makes lambdas a flexible programming tool. However, the lifetime of parameters in a lambda expression is an aspect that requires careful attention, especially when capturing and passing parameters.
1. Lambda Expression Parameter Lifetime
The lifetime of parameters in a lambda expression is typically the same as that of other C++ functions. Parameters exist while the function is being called, and their lifetime ends when the function call terminates. However, due to the possibility of lambdas capturing external variables, the parameter’s lifetime is also affected by how it’s captured.
2. Capturing the Relationship with Parameter Lifecycles
2.1 Capturing External Variables
C++ lambda expressions allow capturing external variables in two ways:
- Capture by Value: When capturing by value, the value of the external variable is copied into the lambda’s internal scope. The lifetime of this copy is controlled by the lambda’s own lifetime.
- Capture by Reference: When capturing by reference, a reference to the external variable is retained. The lambda’s reference points to the original external variable, and its lifetime depends on the external variable’s lifetime.
int x = 10;
auto lambda_by_value = [x]() { std::cout << x << std::endl; }; // Captures a copy of x
auto lambda_by_reference = [&x]() { std::cout << x << std::endl; }; // Captures a reference to x
lambda_by_value(); // Prints 10
lambda_by_reference(); // Prints 10
For captured variables, the lifetimes are as follows:
- Capture by Value: When capturing, the external variable’s value is copied into the lambda, and the copy is destroyed when the lambda’s lifetime ends.
- Capture by Reference: The lambda holds a reference to the external variable; the external variable must be valid at the time the lambda is used, or undefined behavior results.
2.2 Lambda Parameters
Lambda parameters are similar to regular function parameters; their lifetime is limited to the lambda function body. That is, lambda parameters are created when the lambda is called and their lifetime ends when the lambda call finishes.
auto lambda = [](int a, int b) {
std::cout << a + b << std::endl;
};
lambda(5, 10); // a and b are the parameters of the lambda here
In this example, a
and b
are the parameters of the lambda expression, they are created when the lambda is called and destroyed after the lambda executes.
3. Lifecycle Issues When Capturing External Variables
3.1 Whether Captured Variables Can Be Effective Outside Lambda
- Value Capture: Even if the external variable is destroyed after the lambda call, the lambda internally still holds a copy of the external variable. Therefore, the copy within the lambda can be safely used even if the external variable no longer exists.
int x = 10;
auto lambda = [x]() { std::cout << x << std::endl; };
x = 20; // x is modified after the lambda call
lambda(); // Prints 10, captures a copy of x
- Reference Capture: If the external variable is captured by reference, the lambda’s access to this reference depends on the lifetime of the external variable. If the external variable is destroyed before the lambda executes, a dangling reference issue will occur, leading to undefined behavior.
int x = 10;
auto lambda = [&x]() { std::cout << x << std::endl; };
x = 20; // x is modified before the lambda call
lambda(); // Prints 20, captures a reference to x
It’s important to ensure that the external variable is valid when the lambda executes if the execution order of the lambda is uncertain.