什么时候捕获捕获变量?
When are capture variables captured?
我正在制作一个 std::vector
的回调 std::function
,我在理解捕获时遇到了一些困难。如果我通过引用捕获,当我尝试使用它们时,它们似乎超出了范围。如果我按价值捕获,一切正常。
使用这些回调函数的代码需要特定的签名,所以假设我不能修改使用这些函数的代码,我需要坚持使用捕获变量而不是将东西作为函数参数传递。
什么时候 localVar
被捕获了?是在定义 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;
}
创建 lambda 时会捕获 reference。引用对象的 value 永远不会被捕获。当您调用 lambda 时,无论您何时使用它(就像使用任何其他引用一样),它都会使用引用来确定引用对象的值。如果在引用的对象不复存在后使用引用,则您使用的是悬空引用,这是未定义的行为。
在这种情况下,auto my_lambda = [&localVar]()->int
创建了一个 lambda,其中引用名为 localVar
的局部变量 localVar
。
std::cout << fs[i]() << "\n";
调用其中一个 lambda。但是,当 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
阅读相关内容
They seem to be going out of scope when I try to use them if I capture by reference
没错。您创建了一个 lambda,它封装了对局部变量的引用。变量超出范围,使该引用悬空。这与任何其他参考文献没有什么不同。
在您定义 lambda 的地方捕获 "happens" — 这就是它的目的!如果稍后发生,当你调用 lambda 时(哪一次?),你想要捕获的东西早就消失了,或者至少无法访问。
捕获使我们能够 "save" 我们现在可以命名的东西,以备后用。但是如果你通过引用捕获,你最好确保当你使用那个引用时 referred-to 仍然存在。
不过要注意像 这样的怪事。
我正在制作一个 std::vector
的回调 std::function
,我在理解捕获时遇到了一些困难。如果我通过引用捕获,当我尝试使用它们时,它们似乎超出了范围。如果我按价值捕获,一切正常。
使用这些回调函数的代码需要特定的签名,所以假设我不能修改使用这些函数的代码,我需要坚持使用捕获变量而不是将东西作为函数参数传递。
什么时候 localVar
被捕获了?是在定义 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;
}
创建 lambda 时会捕获 reference。引用对象的 value 永远不会被捕获。当您调用 lambda 时,无论您何时使用它(就像使用任何其他引用一样),它都会使用引用来确定引用对象的值。如果在引用的对象不复存在后使用引用,则您使用的是悬空引用,这是未定义的行为。
在这种情况下,auto my_lambda = [&localVar]()->int
创建了一个 lambda,其中引用名为 localVar
的局部变量 localVar
。
std::cout << fs[i]() << "\n";
调用其中一个 lambda。但是,当 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
阅读相关内容They seem to be going out of scope when I try to use them if I capture by reference
没错。您创建了一个 lambda,它封装了对局部变量的引用。变量超出范围,使该引用悬空。这与任何其他参考文献没有什么不同。
在您定义 lambda 的地方捕获 "happens" — 这就是它的目的!如果稍后发生,当你调用 lambda 时(哪一次?),你想要捕获的东西早就消失了,或者至少无法访问。
捕获使我们能够 "save" 我们现在可以命名的东西,以备后用。但是如果你通过引用捕获,你最好确保当你使用那个引用时 referred-to 仍然存在。
不过要注意像