Initializer Capture
In the previous article , we discussed that C++11 supports capturing variables in lambdas. Let’s review the code and rewrite it with deliberately long variable names.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
int main() {
int x_thisIsAnExtremelyLongVariableName = 10;
int y_thisIsAnExtremelyLongVariableName = 20;
auto addXY = [x_thisIsAnExtremelyLongVariableName, y_thisIsAnExtremelyLongVariableName]() -> int {
return x_thisIsAnExtremelyLongVariableName + y_thisIsAnExtremelyLongVariableName;
};
std::cout << "x_thisIsAnExtremelyLongVariableName + y_thisIsAnExtremelyLongVariableName = " << addXY() << std::endl;
return 0;
}
The limitation here is that when capturing variables, you can only use the same variable names for capture. (When the variable names are unusual, they will appear in your lambda function.) Of course, this is not the main reason C++14 aims to improve this.
C++14 introduced a feature called initializer capture, which makes capturing variables in lambda expressions more flexible. It allows you to initialize variables in the capture list, which is particularly useful when you need to use temporary variables or computed values within the lambda expression.
Use Cases
There are four main scenarios:
Complex Expression Calculation
When you need to use complex calculation results within the lambda expression, you can use initializer capture to simplify the code.
1
2
3
4
5
int a = 27;
int b = 37;
auto lambda = [sum = a * b + 3](int x) {
return sum + x;
};
Capturing Temporary Objects
When you need to use temporary objects (such as dynamically allocated memory, temporary strings, etc.) within the lambda expression, you can use initializer capture to ensure that these objects’ lifetimes cover the scope of the lambda expression.
Here’s an example using std::move. If you’re not familiar with it, you can refer to this article.
1
2
3
4
5
6
auto ptr = std::make_unique<int>(48);
auto lambda = [p = std::move(ptr)]() {
std::cout << *p << std::endl;
};
lambda();
Renaming Variables
If you want to use different variable names within the lambda expression, you can achieve this through initializer capture.
For example, using the initial example
1
2
3
4
int x_thisIsAnExtremelyLongVariableName = 100;
auto lambda = [x = x_thisIsAnExtremelyLongVariableName](int y) {
return x + y;
};
Capturing References
Although you can usually capture all external variable references using [&]
, sometimes you need to capture specific variable references. In such cases, initializer capture can also be useful.
1
2
3
4
5
6
int a = 45;
auto lambda = [&ref_a = a]() {
ref_a += 17;
};
lambda();
std::cout << "a: " << a << std::endl;
These scenarios demonstrate the application of initializer capture in various contexts, providing a more flexible and concise programming approach.