I seems to miss some point in lambda mechanism in C++. Here is the code:
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
If there is no mutable
it wouldn't compile because I'm changing init
in lambda.
Now, as I understand lambda is called for each vector's item with a new fresh copy of init
which is 0.
So, 1 must be returned every time.
But the output of this piece of code is:
1 2 3 4 5
0
It looks like generate
captures by copy init
only once at the beginning of its execution. But why? Is it supposed to work like this?
Significance of Lambda Function in C/C++ Lambda Function − Lambda are functions is an inline function that doesn't require any implementation outside the scope of the main program. Lambda Functions can also be used as a value by the variable to store.
A lambda function can have any number of parameters, but the function body can only contain one expression. Moreover, a lambda is written in a single line of code and can also be invoked immediately.
Now, as I understand lambda is called for each vector's item with a new fresh copy of init which is 0.
That is not correct. A lambda is just a another way to make a class and provide an operator()
for it. The []
part of the lambda describes the member variables and whether they are captured by reference or value. The ()
part of the lambda is the parameter list for the operator()
and the {}
part is the body of that function. The mutable
part tells the compiler to make the operator()
non const
as it is const
by default.
So
[init]() mutable { return ++init; }
Becomes
struct compiler_generated_name
{
int init; // we captured by value
auto operator()() // since we used mutable this is non const
{
return ++init;
}
};
I used a struct here for brevity of typing but a lambda is specified as a class type so class
could be used.
This means the init
is the same init
from the last iteration as you only ever capture once. This is important to remember as
auto generate_lambda()
{
int foo = 0;
return [&foo](){ return ++foo; };
}
Will leave you with a dangling reference to foo
when the function returns and using it is undefined behavior.
The lambda is a compiler-generated struct equivalent to:
struct lambda
{
int init = 0; // captured value
auto operator()() // non-const, due to `mutable`
{
return ++init;
}
};
Therefore, init
is captured and copied inside the lambda just once - calling the lambda multiple times will not capture init
again.
You are coping and seeing the initial value of init -- what you you probably want to do acording to what you expect is to capture init
by reference.....
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [&init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With