lambda抓住; c ++ vs elisp

时间:2014-05-08 06:38:33

标签: c++11 lambda elisp c++14

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:
 */

1 个答案:

答案 0 :(得分:1)

是的,这是未定义的行为,因为只要captureFunc()退出(*),lambdas中的引用就会变为悬空。

在你的情况下可能发生的事情是,引用(它们只是引擎盖下的指针)仍然指向第一次调用icaptureFunc()所在的堆栈空间,并且它在captureFunc()的第二次调用中,它最终位于完全相同的位置;因此,所有get1get2set1set2的内部i引用指向同一个(当前未使用过的)位置的净效果是记忆,所以他们互相修改。

当然,以上只是推测,并且可能会在下次(或运行)程序时发生变化,因为 Undefined Behavior is Undefined。

(*)引用C ++ 11,[expr.prim.lambda]§22:

  

[注意:如果通过引用隐式或显式捕获实体,则调用函数调用运算符   在实体的生命周期结束后,相应的 lambda-expression 可能导致未定义   行为。 -end note ]