从Capturing a reference by reference in a C++11 lambda读取答案让我认为以下代码会生成未定义的行为,因为lambda-capture中的i
的结束生命周期。这对C ++ 1y是否正确?我问,因为g++ 4.8.2
可以很好地翻译代码。
#include <iostream>
auto captureFct( ) {
int i=0;
auto set = [&i](int _i){ i=_i; };
auto get = [&i](){ return i; };
return std::pair<decltype(set),decltype(get)>(set,get);
}
int main() {
auto myPair = captureFct();
auto set1 = myPair.first;
auto get1 = myPair.second;
auto myPair1 = captureFct();
auto set2 = myPair1.first;
auto get2 = myPair1.second;
std::cout << "\nget1:" << get1() << " get2:" << get2() << '\n';
set1(1); set2(2);
std::cout << "\nget1:" << get1() << " get2:" << get2();
}
/*
Local Variables:
compile-command: "g++ -std=c++1y lambda.cc -o a.exe && ./a.exe"
End:
*/
输出很有意思:
get1:0 get2:0
get1:2 get2:2
似乎所有lambda都使用相同的引用。
此行为与以下elisp代码的行为不同(尽可能接近c ++代码):
(defun captureFct ()
(lexical-let ((i 0))
(list :set (lambda (_i) (setq i _i))
:get (lambda () i))))
(setq myPair (captureFct))
(setq myPair1 (captureFct))
(message "\nget1: %d get2: %d"
(funcall (plist-get myPair :get))
(funcall (plist-get myPair1 :get)))
(funcall (plist-get myPair :set) 1)
(funcall (plist-get myPair1 :set) 2)
(message "\nget1: %d get2: %d"
(funcall (plist-get myPair :get))
(funcall (plist-get myPair1 :get)))
elisp代码的输出是:
get1: 0 get2: 0
get1: 1 get2: 2
我想我已经知道了答案。但是,无论如何,我发布这个问题,因为对于同时执行elisp和c ++的人来说这很有趣。
最后但并非最不重要的是一个与elisp版本类似的C ++版本:
#include <iostream>
#include <memory>
auto captureFct( ) {
std::shared_ptr<int> pi(new int(0));
auto set = [pi](int _i){ *pi=_i; };
auto get = [pi](){ return *pi; };
return std::pair<decltype(set),decltype(get)>(set,get);
}
int main() {
auto myPair = captureFct();
auto set1 = myPair.first;
auto get1 = myPair.second;
auto myPair1 = captureFct();
auto set2 = myPair1.first;
auto get2 = myPair1.second;
std::cout << "\nget1:" << get1() << " get2:" << get2() << '\n';
set1(1); set2(2);
std::cout << "\nget1:" << get1() << " get2:" << get2();
}
/*
Local Variables:
compile-command: "g++ -std=c++1y lambda.cc -o a.exe && ./a.exe"
End:
*/
答案 0 :(得分:1)
是的,这是未定义的行为,因为只要captureFunc()
退出(*),lambdas中的引用就会变为悬空。
在你的情况下可能发生的事情是,引用(它们只是引擎盖下的指针)仍然指向第一次调用i
时captureFunc()
所在的堆栈空间,并且它在captureFunc()
的第二次调用中,它最终位于完全相同的位置;因此,所有get1
,get2
,set1
,set2
的内部i
引用指向同一个(当前未使用过的)位置的净效果是记忆,所以他们互相修改。
当然,以上只是推测,并且可能会在下次(或运行)程序时发生变化,因为 Undefined Behavior is Undefined。
(*)引用C ++ 11,[expr.prim.lambda]§22:
[注意:如果通过引用隐式或显式捕获实体,则调用函数调用运算符 在实体的生命周期结束后,相应的 lambda-expression 可能导致未定义 行为。 -end note ]