1

Paul Preney によって作成されたExpression templates および C++11の式テンプレート コードに何らかの形で触発されて、以下をテストすることにしました。

template<typename T>
struct X
{
    X(T t) : t(std::forward<T>(t)) {}

    T t;
};

template<typename T>
auto CreateX(T&& t) -> X<decltype(std::forward<T>(t))>
{
    return X<decltype(std::forward<T>(t))>(std::forward<T>(t));
}

次に、それを使用して、次のようにX<const vector<int>&>andのインスタンスを生成しました。X<vector<int>&&>

int main()
{
    int vec = {1,2,3,4};

    auto x1 = CreateX(vec);
    auto x2 = CreateX(vector<int>{5,6,7,8});

    cout << "x1: "; for(auto x : x1.t) cout << x << " "; cout << endl;
    cout << "x2: "; for(auto x : x2.t) cout << x << " "; cout << endl;
}

出力は次のとおりです。

x1: 1 2 3 4 
x2: 0 0 33 0 0 0 7 8 

これは、一時的な有効期間vector<int>{5,6,7,8}が延長されておらず、右辺値参照メンバーが別のX::tものにバインドされていることを示しています。

さて、この回答から右辺値への参照をconstするクラスデータメンバーの寿命は何ですか? 、これが予想される動作であることはわかっています。

ただし、ここでの質問は、右辺値参照メンバーが存在する限り一時ベクトルの存在を許可するExpression テンプレートと C++11の Paul Preney のコードの違いは何ですか? 一時的に作成されるケース 2 を参照してください。

どうやら、同じ構造がここで使用されているようですが、おそらく何かが欠けています。


編集: 以下の R. Martinho Fernandes の回答に基づいて、次のことを試しました。

int main()
{
    using namespace std;

    auto expr = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2};

    cout << "vec1: "; for(int i = 0; i < 3; ++i) cout << expr.le()[i] << " "; cout << endl;
    cout << "vec2: "; for(int i = 0; i < 3; ++i) cout << expr.re()[i] << " "; cout << endl;
}

そして、これが有効なコードであることがわかります。

vec1: 1.0 1.1 1.2
vec2: 2.0 2.1 2.2

したがって、式テンプレートに格納されている参照は明らかにダングリングではありません。ここで何が起こっているのですか?

4

2 に答える 2

3

右辺値参照メンバーが存在する限り、一時的なベクトルの存在を許可する Expression テンプレートと C++ 11 の Paul Preney のコードの違いは何ですか?

そのようなことを許すものは何もありません。

そこにある一時ベクトルは、ローカル参照変数にバインドされていない他の一時ベクトルと同様に、完全な式の終わりまで存在します。Paul のコードではこれで十分です。なぜなら、コードは式ツリーを実際の にすぐに実体化するmath_vectorからです。その後、一時変数はもう必要ありません。

Paul のコードは式テンプレート ノード ( ) をどこにも格納しませんがmath_vector_expr、あなたのコードは 1 つの ( X) を として格納しますx2。これは の既知の問題autoです。式テンプレートを使用すると、式ツリーが格納されるため、式ツリーがすぐにぶら下がる参照を含む可能性が高いため、間違った動作をします。

それを完全に明確にするために、以下は問題ありません。

math_vector<3> result =
    math_vector<3>{1.0, 1.1, 1.2} +
    math_vector<3>{2.0, 2.1, 2.2} +
    math_vector<3>{3.0, 3.1, 3.2} +
    math_vector<3>{4.0, 4.1, 4.2}
; // no references are held to any temporaries past this point

以下はダメです。

math_vector_expr<3> result =        // or auto
    math_vector<3>{1.0, 1.1, 1.2} +
    math_vector<3>{2.0, 2.1, 2.2} +
    math_vector<3>{3.0, 3.1, 3.2} +
    math_vector<3>{4.0, 4.1, 4.2}
; // result now holds references to those temporaries
于 2013-08-07T11:51:59.503 に答える
2

R. Martinho Fernandes が彼の回答で述べているように、問題は、「式ツリー」で右辺値への参照をキャプチャし、その参照対象よりも長く存続することです。解決策は、左辺値への参照のみを格納し、右辺値参照によって渡されたオブジェクトを直接キャプチャすることです。つまり、を参照ではなく式ツリーに保存します ( Coliru のライブ コード)。

template<typename T>
struct X
{
    X(T t) : t(std::move(t)) {}

    T t;
};

// Capture lvalue references
template<typename T>
X<const T&> CreateX(const T& t)
{
    return X<const T&>(t);
}

// Copy rvalue references
template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value,X<T>>::type
CreateX(T&& t)
{
    return X<T>(std::move(t));
}

ユーザーが左辺値参照を渡した場合、その参照先が式ツリー オブジェクトよりも長く存続することを確認するのはユーザーの責任です。

于 2013-08-07T13:23:36.270 に答える