我正在制作std::vector
回调std::function
,我在理解捕获时遇到了一些麻烦。当我尝试使用它们时,它们似乎超出了范围,如果我通过引用捕获它们。如果我按价值捕捉,一切正常。
使用这些回调函数的代码需要一定的签名,因此假设我无法修改使用这些代码的代码,我需要坚持使用捕获变量而不是将事物作为函数参数传递。
何时被捕获?localVar
?是在定义lambda还是调用lambda时?答案是否会根据我是按值还是参考来捕获?
这是我想要了解的一个小例子:
#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;
}
答案 0 :(得分:8)
创建lambda时会捕获引用。永远不会捕获引用对象的值。当你调用lambda时,它会使用引用来确定引用对象的值(无论何时使用它)(比如使用任何其他引用)。如果在引用的对象不再存在后使用引用,则使用的是悬空引用,它是未定义的行为。
在这种情况下,auto my_lambda = [&localVar]()->int
会为局部变量localVar
创建一个名为localVar
的引用的lambda。
std::cout << fs[i]() << "\n";
调用了一个lambdas。但是,当lambda执行return localVar+100;
时,它会尝试将引用localVar
用于局部变量localVar
(第一个for
循环的本地变量)但是局部变量不再存在。你有不确定的行为。
如果删除&符号并按值(localVar
)取auto my_lambda = [localVar]()->int
,则会在创建lambda时捕获值的副本。由于它是副本,原始localVar
的内容并不重要。
您可以在http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture
了解相关信息答案 1 :(得分:4)
当我尝试使用它们时,如果我通过引用
捕获它们,它们似乎超出了范围
没错。您创建了一个lambda,它封装了对局部变量的引用。变量超出了范围,使该引用悬空。这与任何其他参考文献没有什么不同。
在定义lambda的位置捕获“发生” - 这就是它的目的!如果它发生在以后,当你调用lambda时(那个时间?),你想要捕获的东西早就消失了,或者至少是无法到达的。
捕捉允许我们“保存”我们现在可以命名的内容,以供日后使用。但是如果你通过引用捕获,那么当你使用该引用时,你最好确保引用的东西仍然存在。
请注意像this这样的奇怪现象。