I thought I understood how capture by reference works in C++ until I faced this situation:
auto inrcrementer = []() {
int counter = 0;
return [&counter]() {
return counter++;
};
};
int main() {
auto inc = inrcrementer();
cout << inc() << ", " << inc() << ", " << inc() << ", " << endl;
return 0;
}
I was expecting this to return 0, 1, 2 but it returns 0, 32765, 32765. Why?
Moreover, if I change it to this:
auto inrcrementer = []() {
int counter = 0;
return [counter]() mutable {
return counter++;
};
};
int main() {
auto inc = inrcrementer();
cout << inc() << ", " << inc() << ", " << inc() << ", " << endl;
return 0;
}
It is fixed and it returns as expected. What is the difference between the two?
>Solution :
The problem is that in your first example, the lambda stores a reference to, rather than a copy of, counter. But counter goes out of scope when incrementer returns so that reference is left dangling. Then, when you invoke the lambda returned by incrementer, that reference is invalid.
What this usually means in practise is that it is pointing at an area of the stack that has been / is being used for something else. That’s why you get weird results.
In your second example, the lambda has its own copy of counter, and that remains in scope for as long as the lambda remains in existence. Since you have declared the lambda mutable, it can do whatever it likes with its (private) copy of counter without anybody else messing with it.
So capture by reference can be dangerous. Use with care! A reference is just a pointer really, under the skin, but because it doesn’t look like one it’s easy to slip up.