Управление памятью для лямбды в C ++ 11

Может ли кто-нибудь описать, почему этот код не работает (на ошибках 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

6

Решение

Это проблемный код:

[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. Вернуть результат прямо из задания: до назначения 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 и используйте его вместо ссылки на поле, которое принадлежит перезаписанному закрытию:

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

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]