6

誰かがこのコードが機能しない理由を説明できますか (呼び出しから戻る前の GCC4.7.3 seg-faults で)?

#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()>オブジェクトには、古いクロージャー (エンドライフタイムで選択された値) を破棄し、新しい値の所有権を取得する代入演算子があります。

PSこの質問は、C ++ 11での怠惰に関する質問を読んだ後に提起されました

4

1 に答える 1

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. 派手になる代わりに、単に return 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();
    
于 2013-10-01T22:38:48.260 に答える