xvalue的生命周期是否延伸到引用?

时间:2013-07-21 03:22:28

标签: c++ c++11 language-lawyer

显然,编译器之间存在一些混淆和差异:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/3c754c4e-5471-4095-afae-795c1f411612/rvalue-refs-extended-lifetime-inconsistent-with-gccstandard

根据这篇文章:

What are rvalues, lvalues, xvalues, glvalues, and prvalues?

Xvalues是rvalues(以及prvalues),标准说:

  

第二个上下文是引用绑定到临时的。该   临时引用的临时或临时的   引用绑定到的子对象的完整对象   在参考文件的生命周期内持续存在,除了:

但是有些帖子对此提出质疑:

Do rvalue references allow dangling references?

What is an example of a difference in allowed usage or behavior between an xvalue and a prvalue FOR NON-POD objects?

有人可以澄清一下这个问题。 MSVC是否适合一次?

1 个答案:

答案 0 :(得分:3)

Xvalues可能是rvalues,但这并不意味着它们是 temporaries 。临时工作的终生延伸来自于他们是临时,而不是他们的价值范畴。

我刻意尝试不知道处理运算符的顺序(这样,我强迫自己编写使用显式括号或不关心顺序的代码)。您在此处转载的特定adder示例代码非常关注:

template <class T>
struct addable
{
    friend T operator +( const T& lhs, const T& rhs )
    {
        return std::move(T(lhs) += rhs); 
    }
    friend T operator +( const T& lhs, T&& rhs )
    {
        return std::move(T(lhs) += std::move(rhs));
    }   
    friend T&& operator +( T&& lhs, const T& rhs ) 
    {
        return std::move(lhs += rhs);
    }               
    friend T&& operator +( T&& lhs, T&& rhs ) 
    {
        return std::move(lhs += std::move(rhs)); 
    }
};

如果+运算符从右到左完成,那么t1 + t2 + t3将会计算到t1 + (t2 + t3)t2 + t3将调用第一个重载,从而生成临时,从而产生t1 + temp。由于临时将优先绑定到r值引用,该表达式将调用第二个重载,这将返回临时。

但是,如果+运算符从左到右工作,则会得到(t1 + t2) + t3。这给了我们temp + t1,这会导致问题。它将调用第三个重载。该函数的lhs参数是T&&,是对临时的引用。您返回相同的参考。这意味着您已返回对临时的引用。但是C ++并不知道;它只知道你正在返回对某事的引用。

然而,在确定最终表达式(赋值给新变量,值类型或引用类型)之后,“某些东西”将被销毁。记住:C ++不知道这个函数会返回对它的第一个参数的引用。所以它无法知道传递给函数操作数的临时生命周期需要扩展到存储返回引用的生命周期。

顺便说一句,这就是为什么表达式树可能会因auto而潜伏而存在​​危险。因为创建的内部临时变量不能由存储在各种对象中的新临时对象或引用保留。 C ++没有办法做到这一点。

所以谁的权利取决于运营商的解决顺序。但是,我更喜欢我的解决方案:不要依赖语言的这些角落而只是解决它们。停止从这些重载返回T&&,只需将值移到临时值即可。这样,它保证正常工作,您不必经常检查标准,以确保您的代码正常工作。

另外,另外,我认为操作符+实际上修改其中一个参数有点粗鲁。

但是,如果你坚持要知道谁是对的,那就是海湾合作委员会。从5.7节,p1:

  

加法运算符+和 - 从左到右分组。

是的,它应该不起作用。

注意:Visual Studio允许T &r2 = t1 + t2 + t3;编译为(非常恼人的)语言扩展。你应该得到它的警告。