2013-10-01 72 views
6

有人可以描述为什么这段代码不起作用(在GCC4.7.3 seg-faults从调用返回之前)?C++中lambda的内存管理11

#include <iostream> 
#include <functional> 
#include <memory> 

using namespace std; 

template<typename F> 
auto memo(const F &x) -> std::function<decltype(x())()> { 
    typedef decltype(x()) return_type; 
    typedef std::function<return_type()> thunk_type; 
    std::shared_ptr<thunk_type> thunk_ptr = std::make_shared<thunk_type>(); 

    *thunk_ptr = [thunk_ptr, &x]() { 
     cerr << "First " << thunk_ptr.get() << endl; 
     auto val = x(); 
     *thunk_ptr = [val]() { return val; }; 
     return (*thunk_ptr)(); 
    }; 

    return [thunk_ptr]() { return (*thunk_ptr)(); }; 
}; 

int foo() { 
    cerr << "Hi" << endl; 
    return 42; 
} 

int main() { 
    auto x = memo(foo); 
    cout << x() << endl ; 
    cout << x() << endl ; 
    cout << x() << endl ; 
}; 

我原来的假设:

    每个
  1. std::function<T()>是有点儿参考/ shared_ptr的一些对象,表示闭合。即收购价值的生命周期受其限制。
  2. std::function<T()>对象具有赋值运算符,该运算符将放弃旧闭包(最终生命期拾取值)并将获得新值的所有权。

P.S.

[thunk_ptr, &x]() { 
    auto val = x(); 
    *thunk_ptr = [val]() { return val; }; 
    return (*thunk_ptr)(); // <--- references a non-existant local variable 
} 

的问题是,当地thunk_ptr是从上下文的副本:我读question about lazy in C++11

+0

嗯,不知何故'thunk_ptr'最终拥有本身。这看起来不正确。 –

+0

@KerrekSB,是的,同意,但这应该得到内存泄漏,而不是seg-fault – ony

+0

返回(* thunk_ptr)();的原因是返回引用字段的封闭[val](){return VAL; }如果引用添加到'thunk_type'。请参阅(在我的答案中替代实现'lazy')[http://stackoverflow.com/a/19125422/230744] – ony

回答

5

后,这是有问题的代码提出这个问题。也就是说,在赋值*thunk_ptr = ...中,thunk_ptr引用了函数对象拥有的副本。但是,通过赋值功能对象不再存在。也就是说,在下一行thunk_ptr指的是一个刚毁坏的对象。

有解决该问题的几种方法:

  1. 非但没有看中的,只是返回val。这里的问题是return_type可能是一个引用类型,它会导致这种方法失败。
  2. 返回直接从分配结果:事先分配thunk_ptr到现在还活着,并在转让后仍回到std::function<...>()对象的引用:

    return (*thunk_ptr = [val](){ return val; })(); 
    
  3. 安全的thunk_ptr副本,并使用该副本,以调用函数对象在return声明:

    std::shared_ptr<thunk_type> tmp = thunk_ptr; 
    *tmp = [val]() { return val; }; 
    return (*tmp)(); 
    
  4. 保存的参考副本std::function,并用它研究所参照外地的EAD属于覆盖封闭:

    auto &thunk = *thunk_ptr; 
    thunk = [val]() { return val; }; 
    return thunk(); 
    
+0

整个想法是对'val'进行惰性计算。这就是为什么有两个“延续”(评估和返回评估值)。请注意,我使用'std :: make_shared'在堆上分配'std :: function ',稍后我将一个shared-ptr复制到第一个闭包(增量引用),之后我再次在第二个闭包中复制该shared-ptr (总共+2裁判)和退出功能后,我放下一个裁判。共有2个参考文献:其中一个由第一个关闭(通过@KerrekSB提到的循环引用)和一个由关闭关闭(可能被释放并下降到1个参考)保存。我看不到你说的话。 – ony

+0

@ony:问题是**不是**,而是使用'std :: function <...>'对象,但是替换lambda中的'std :: shared_ptr <...>'的本地副本:'std :: finction <...>'对象是这个lambda的唯一所有者。当您将'std :: function <...>'对象分配给我们销毁的lambda并且使用'std :: shared_ptr <...>'('thunk_ptr')。当你想访问'std :: function <...>'对象时,你需要保留'thunk_ptr'的副本,因为'thunk_ptr'被破坏(对'std :: function <...>'的赋值就像'delete this'一样)。 ......并且我明白这是干什么的意图! –

+0

是的,你说得对,关闭内部的'thunk_ptr'指的是闭包对象内的字段的地址,该对象被那个赋值以新的闭包销毁。对不起,我误解了你的答案。当我让我在这里再次投票时,我会等待。你能编辑你的答案,只是为了让我撤销我的失望。 – ony