繁体   English   中英

数据成员和右值生存期

[英]Data member and rvalue life-time

由Paul Preney编写的Expression template和C ++ 11中的expression template代码启发了我,决定测试以下内容:

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>&>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引用一个右值的类数据成员的生存期是多少? ,我知道这是预期的行为。

但是,这里的问题是: 表达式模板和C ++ 11中的保尔·普雷尼的代码有什么不同,只要存在右值引用成员,它们就允许存在临时矢量? 请参阅他的案例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

因此,显然存储在表达式模板中的引用不是悬挂的。 这里发生了什么?

Paul Preney的Expression模板和C ++ 11中的代码有什么不同,只要存在rvalue-references成员,它们就允许存在临时矢量?

没有什么允许这样的事情。

那里的临时向量一直存在到完整表达式的结尾,就像其他任何未绑定到局部引用变量的临时向量一样。 这在Paul的代码中就足够了,因为代码会立即将表达式树具体化为实际的math_vector ,之后便不再需要临时对象了。

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在他的回答中指出的那样,问题是您在“表达式树”中捕获了对rvalue的引用,这些引用超出了其引用对象的寿命。 解决方案是仅存储对左值的引用,并直接捕获由右值引用传递的对象。 换句话说,将它们的存储在表达式树中,而不是通过引用存储( 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));
}

如果用户向您传递了一个左值引用,则她有责任确保引用对象的寿命超过了表达式树对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM