Может ли кто-нибудь описать, почему этот код не работает (на ошибках 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 ;
};
Мои оригинальные предположения:
std::function<T()>
это своего рода ссылка / shared_ptr на некоторый объект, который представляет закрытие. То есть время жизни подобранной ценности ограничено этим.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
относится к только что уничтоженному объекту.
Существует несколько подходов к решению проблемы:
val
, Проблема здесь в том, что return_type
может быть ссылочным типом, который приведет к сбою этого подхода.Вернуть результат прямо из задания: до назначения thunk_ptr
еще жив, и после назначения он все еще возвращает ссылку на std::function<...>()
объект:
return (*thunk_ptr = [val](){ return val; })();
Безопасная копия thunk_ptr
и использовать эту копию для вызова объекта функции в return
заявление:
std::shared_ptr<thunk_type> tmp = thunk_ptr;
*tmp = [val]() { return val; };
return (*tmp)();
Сохранить копию ссылки на std::function
и используйте его вместо ссылки на поле, которое принадлежит перезаписанному закрытию:
auto &thunk = *thunk_ptr;
thunk = [val]() { return val; };
return thunk();
Других решений пока нет …