C++11 中 Lambda 的内存管理

memory management for lambda in C++11

本文关键字:内存 管理 Lambda C++11      更新时间:2023-10-16

有人可以描述为什么这段代码不起作用(在从呼叫返回之前在GCC4.7.3赛格错误上(?

#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()>对象具有赋值运算符,该运算符将放弃旧的闭包(结束生命周期选取的值(并将获得新值的所有权。

附言这个问题是在我读到C++11中关于懒惰的问题后提出的

这是有问题的代码:

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

问题是本地thunk_ptr是上下文的副本。也就是说,在赋值*thunk_ptr = ...中,thunk_ptr 引用函数对象拥有的副本。但是,通过赋值,函数对象将不复存在。也就是说,在下一行thunk_ptr指的是刚刚销毁的对象。

有几种方法可以解决此问题:

  1. 与其花哨,不如返回val.这里的问题是return_type可能是会导致此方法失败的引用类型。
  2. 直接从赋值
  3. 返回结果:在赋值之前,thunk_ptr仍然处于活动状态,在赋值之后,它仍然返回对std::function<...>()对象的引用:

    return (*thunk_ptr = [val](){ return val; })();
    
  4. 保护thunk_ptr的副本,并使用此副本调用return语句中的函数对象:

    std::shared_ptr<thunk_type> tmp = thunk_ptr;
    *tmp = [val]() { return val; };
    return (*tmp)();
    
  5. 保存对std::function引用的副本并使用它,而不是引用属于覆盖闭包的字段:

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