Reputation: 2087
I am making a std::vector
of callback std::function
s, and I'm having a little trouble understanding the captures. They seem to be going out of scope when I try to use them if I capture by reference. If I capture by value, everything works.
The code that uses these callback functions expects a certain signature, so assuming I can't modify the code that's using these, I need to stick with capture variables instead of passing things as function arguments.
When is localVar
being captured? Is it when the lambda is defined, or when it is called? Does the answer change depending on whether I capture by value or reference?
Here's a little example that I would like to understand:
#include <iostream>
#include <functional>
#include <vector>
int main(int argc, char **argv)
{
int n(5);
// make a vector of lambda functions
std::vector<std::function<const int(void)> > fs;
for(size_t i = 0; i < n; ++i){
int localVar = i;
auto my_lambda = [&localVar]()->int // change &localVar to localVar and it works
{
return localVar+100;
};
fs.push_back(my_lambda);
}
// use the vector of lambda functions
for(size_t i = 0; i < n; ++i){
std::cout << fs[i]() << "\n";
}
return 0;
}
Upvotes: 1
Views: 170
Reputation: 385174
They seem to be going out of scope when I try to use them if I capture by reference
That's right. You created a lambda that encapsulates a reference to a local variable. The variable went out of scope, leaving that reference dangling. This is no different to any other reference.
Capturing "happens" at the point where you define the lambda — that is the purpose of it! If it occurred later, when you call the lambda (which time?), the things you wanted to capture would be long gone, or at least unreachable.
Capturing allows us to "save" things that we can name now, for later. But if you capture by reference, you'd better ensure the thing referred-to still exists when you come to use that reference.
Watch out for weirdnesses like this, though.
Upvotes: 4
Reputation: 29022
The reference is captured when you create the lambda. The value of the referred object is never captured. When you call the lambda, it will use the reference to determine the referred object's value whenever you use it (like using any other reference). If you use the reference after the referred object ceases to exist, you are using a dangling reference, it's undefined behavior.
In this case, auto my_lambda = [&localVar]()->int
creates a lambda with a reference named localVar
to the local variable localVar
.
std::cout << fs[i]() << "\n";
calls one of the lambdas. However, when the lambda executes return localVar+100;
, it's trying to use the reference localVar
to the local variable localVar
(local to the first for
loop) but that local variable no longer exists. You have undefined behavior.
If you drop the ampersand and take localVar
by value (auto my_lambda = [localVar]()->int
), you will instead capture a copy of the value as it is at the moment the lambda is created. Since it's a copy, it doesn't matter what happens to the original localVar
.
You can read about this at http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture
Upvotes: 8