Ash*_*egi 12 c++ lambda local c++11
我通过引用两个lambda传递我的局部变量.我在函数范围之外调用这些lambdas.这是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;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果不是,则一个lambda中的变化不会反映在其他lambda中.
我是否误解了lambda的背景中的传递?
我正在写变量,它似乎工作正常,输出没有运行时错误
2 0.如果它工作,那么我会期望输出2 1.
cdh*_*wie 24
是的,这会导致未定义的行为.lambdas将引用超出范围的堆栈分配对象.(从技术上讲,据我所知,行为定义直到lambdas访问a和/或b.如果你从不调用返回的lambdas那么就没有UB.)
这是未定义的行为,就像它返回对堆栈分配的本地的引用的未定义行为一样,然后在本地超出作用域之后使用该引用,除了在这种情况下它被lambda稍微混淆了.
此外,请注意调用lambdas的顺序是未指定的 - 编译器f.second()之前可以自由调用,f.first()因为它们都是同一个完整表达式的一部分.因此,即使我们解决因使用引用销毁对象不确定的行为,无论是2 0和2 1是仍然从这项计划中有效的输出,这你要看在你的编译器决定执行lambda表达式的顺序.请注意,这不是未定义的行为,因为编译器根本无法执行任何操作,而只是在决定执行某些操作的顺序方面有一些自由.
(请记住,<<在您的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;
}
Run Code Online (Sandbox Code Playgroud)
(见它跑.)
650*_*502 10
不幸的是,C++ lambdas可以通过引用捕获,但不能解决" 向上的funarg问题 ".
这样做需要在"单元格"中分配捕获的本地,并在重新分配时分配垃圾收集或引用计数.C++没有这样做,不幸的是,这使得C++ lambdas比其他语言(如Lisp,Python或Javascript)更有用,更危险.
更具体地说,根据我的经验,您应该不惜一切代价避免通过引用(即使用[&](…){…}表单)隐式捕获在本地范围内存活的lambda对象,因为这是稍后在维护期间随机段错误的处方.
始终仔细规划捕获的内容以及捕获的参考文献的生命周期.
当然,通过引用捕获所有内容是安全的,[&] 如果您正在做的只是使用同一范围内的lambda将代码传递给算法,std::sort而不必在函数之外定义命名的比较器函数.
有时可以工作的方法是将值 a 捕获shared_ptr到堆分配状态.这基本上是手工实现Python自动执行的操作(但要注意引用循环以避免内存泄漏:Python有垃圾收集器,C++没有).