我通过引用两个 lambda 来传递我的局部变量。我在函数范围之外调用这些 lambda。这是 undefined
吗?
std::pair<std::function<int()>, std::function<int()>> addSome() {
int a = 0, b = 0;
return std::make_pair([&a,&b] {
++a; ++b;
return a+b;
}, [&a, &b] {
return a;
});
}
int main() {
auto f = addSome();
std::cout << f.first() << " " << f.second();
return 0;
}
如果不是,那么一个 lambda 的变化不会反射(reflect)在另一个 lambda 中。
我是否误解了 lambdas 上下文中的传递引用?
我正在写入变量,它似乎工作正常,没有输出运行时错误
2 0
。如果它有效,那么我希望输出 2 1
.
最佳答案
是的,这会导致未定义的行为。 lambda 将引用超出范围的堆栈分配对象。 (从技术上讲,据我了解,行为是在 lambda 访问 a
和/或 b
之前定义的。如果您从不调用返回的 lambda,则没有 UB。)
这是未定义的行为,与返回对堆栈分配的本地的引用然后在本地超出范围后使用该引用的未定义行为相同,除了在这种情况下它被 lambda 混淆了一点.
此外,请注意调用 lambda 的顺序是未指定的——编译器可以自由调用 f.second()
之前 f.first()
因为两者都是同一个完整表达式的一部分。因此,即使我们修复了使用对已销毁对象的引用导致的未定义行为,2 0
和 2 1
仍然是这个程序的有效输出,你得到的输出取决于你的编译器决定执行 lambdas 的顺序。请注意,这不是未定义的行为,因为编译器不能做任何事情,它只是在决定执行顺序方面有一些自由一些事情。
(请记住,<<
函数中的 main()
正在调用自定义的 operator<<
函数,并且未指定计算函数参数的顺序。编译器可以自由地发出计算所有函数的代码以任意顺序在同一个完整表达式中的参数,约束是必须在调用该函数之前评估函数的所有参数。)
要解决第一个问题,请使用 std::shared_ptr
创建一个引用计数的对象。通过值捕获这个共享指针,只要它们(及其任何拷贝)存在,lambdas 就会使指向的对象保持事件状态。这个堆分配的对象是我们存储a
的共享状态的地方。和 b
.
要解决第二个问题,请在单独的语句中评估每个 lambda。
这里是你的代码改写了未定义的行为修复,f.first()
保证在 f.second()
之前调用:
std::pair<std::function<int()>, std::function<int()>> addSome() {
// We store the "a" and "b" ints instead in a shared_ptr containing a pair.
auto numbers = std::make_shared<std::pair<int, int>>(0, 0);
// a becomes numbers->first
// b becomes numbers->second
// And we capture the shared_ptr by value.
return std::make_pair(
[numbers] {
++numbers->first;
++numbers->second;
return numbers->first + numbers->second;
},
[numbers] {
return numbers->first;
}
);
}
int main() {
auto f = addSome();
// We break apart the output into two statements to guarantee that f.first()
// is evaluated prior to f.second().
std::cout << f.first();
std::cout << " " << f.second();
return 0;
}
( See it run .)
关于c++ - Lambda 和通过引用局部变量 : Accessing after the scope 捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27775233/