C++ 数据成员和右值生命周期

C++ 数据成员和右值生命周期,c++,c++11,rvalue,expression-templates,C++,C++11,Rvalue,Expression Templates,受Paul Preney在中编写的表达式模板代码的启发,我决定测试以下内容: 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))> { retu

受Paul Preney在中编写的表达式模板代码的启发,我决定测试以下内容:

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));
}
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;
}
这表明临时向量{5,6,7,8}的生存时间没有被延长,并且右值引用成员X::t绑定到其他对象

好的,从这个答案,我知道这是预期的行为

然而,这里的问题是:Paul Preney的代码中有什么不同之处,只要右值引用成员存在,就允许临时向量存在?参见他的案例2,其中创建了临时表

显然,这里使用了相同的构造,但我可能遗漏了一些东西

编辑: 根据下面R.Martinho Fernandes的回答,我尝试了以下方法:

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));
}
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;
}
因此,显然,表达式模板中存储的引用不是悬空的。这是怎么回事

Paul Preney在Expression templates和C++11中的代码有什么不同之处,只要右值引用成员存在,就允许临时向量存在

这样的事是不允许的

在完整表达式结束之前,存在的临时向量与未绑定到局部引用变量的任何其他临时向量一样。这在Paul的代码中已经足够了,因为代码立即将表达式树具体化为实际的数学向量,之后就不再需要临时变量了

Paul的代码不在任何位置存储任何表达式模板节点math_vector_expr,而您的代码将一个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
Paul Preney在Expression templates和C++11中的代码有什么不同之处,只要右值引用成员存在,就允许临时向量存在

这样的事是不允许的

在完整表达式结束之前,存在的临时向量与未绑定到局部引用变量的任何其他临时向量一样。这在Paul的代码中已经足够了,因为代码立即将表达式树具体化为实际的数学向量,之后就不再需要临时变量了

Paul的代码不在任何位置存储任何表达式模板节点math_vector_expr,而您的代码将一个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

正如R.Martinho Fernandes在他的回答中所说的那样,问题在于在表达式树中捕获对超过其引用的右值的引用。解决方案是只存储对左值的引用,并捕获由右值引用直接传递的对象。换句话说,将其值存储在表达式树中,而不是通过引用:


如果用户向您传递左值引用,则她有责任确保引用对象比表达式树对象更长寿。

正如R.Martinho Fernandes在其回答中所述,问题在于,您在表达式树中捕获比其引用对象更长寿的右值引用。解决方案是只存储对左值的引用,并捕获由右值引用直接传递的对象。换句话说,将其值存储在表达式树中,而不是通过引用:


如果用户向您传递一个左值引用,则她有责任确保引用对象比表达式树对象更长寿。

挑剔:在X的构造函数中,std::forward只是一种冗长的说法std::move。decltypestd::forwardt也是一种非常冗长的表示T&&.Nitpick的方式:在X的构造函数中,std::forward只是一种冗长的表示std::move的方式。decltypestd::forwardt也是一种非常冗长的表示T&&@Martinho,请看一下我的编辑。事实证明,您的第二个代码段是有效的代码,我甚至可以访问表达式模板中的引用并打印它们的内容。@Allan是的,它在编译时是有效的。但这并不能保证有效。你能打印内容是个意外。有时,您似乎仍然可以访问悬挂引用,但这只是因为您运气不好:行为未定义,可能会停止使用较小的更改或不同的编译器标志或版本。碰巧您可以出于与此处解释类似的原因打印这些内容:感谢您的澄清。@Martinho,请查看我的编辑。事实证明,您的第二个代码片段是有效的代码,我甚至可以访问表达式t中的引用
放置并打印它们的内容。@Allan是的,它在编译的意义上是有效的。但这并不能保证有效。你能打印内容是个意外。有时,您似乎仍然可以访问悬挂引用,但这只是因为您运气不好:行为未定义,可能会停止使用较小的更改或不同的编译器标志或版本。碰巧您可以出于与此处所述类似的原因打印这些内容:感谢您的澄清。