I wanted to undersatnd variable i scope w.r.to lambda function. i is captured as by value So incrementation should only happen internally and it should not effect the global i value. So i expected output 1 1 1. But the output is 1 2 2
int i = 0;
auto a = [i]() mutable { cout << i << endl; };
a(); // i copies by value , So i value get changed internally in lambda function scope
auto b = a;
a(); // here why it is giving 2 instead of 1 ???
b();
What am i missing here ?
CodePudding user response:
int i = 0;
auto a = [i]() mutable { cout << i << endl; }; // i is captured in a with value 0
a(); // i captured by a becomes 1
auto b = a; // b is copied from a, the captured i in b is copied with value 1 too
a(); // i captured by a becomes 2
b(); // i captured by b becomes 2
CodePudding user response:
Result of lambda expression is an object with operator()
. Code here
int i = 0;
auto a = [i]() mutable { cout << i << endl; };
a();
a();
results in behaviour roughly similar to this:
int i = 0;
struct local_lambda_01 {
private: // some older compilers skip this and in result there is an
// implementation detail that you can access following members
int i = ::i; // this is result of capture by value.
public:
void operator() { // it's not `const` because lambda is `mutable`
cout << i << endl; // it's `this->i`
}
} a; // a variable of local_lambda_01 type;
a(); // call to operator(), increments member i of local_lambda_01
a();
A copy of variable i
's value was stored at point of creation of callable a
instance. As lambda was declared mutable
, operator()
is not const-declared and can modify stored values. It doesn't change the original.
CodePudding user response:
A good way to explain this behavior: think of your lambda as if it were a class (it is, actually), and think of the captured variable as a private class member.
In other words, this:
auto i = 0;
auto f = [i]() mutable { std::cout << i << std::endl; };
it's a lot like this:
class
{
int i = 0;
public:
void operator() ()
{
std::cout << i << std::endl;
}
} f;
In both cases, think as f
as an instance that has its own private i
member, so whenever you call f()
you basically do f.i
.
Assigning a copy to another variable, creates another instance with its own i
member initialized with the current value of i
in the copied instance.
You can have a "constructor" as well, like
auto build = []() {
auto i = 0;
return [i]() mutable { std::cout << i << std::endl; };
};
now you can have fresh instances where i
starts at 0
auto a = build();
a(); // a.i is 1
a(); // a.i is 2
auto b = build();
b(); // b.i is 1