什么是 rvalues、lvalues、xvalues、glvalues 和 prvalues?很好地概述了右值/左值的分类,最近对该问题的回答之一(https://stackoverflow.com/a/9552880/368896)强调了纯右值“类似于”旧式右值的观点,而新的 xvalues 允许“类似左值”的行为。
但是,请考虑以下代码:
class X {};
X foo() { return X(); }
int main()
{
foo() = X(); // foo() is a prvalue that successfully appears on the lhs
}
在此示例中,表达式foo()
是出现在左侧的纯右值,并接受赋值。
这让我开始思考——“xvalues”与“prvalues”不同的逻辑,因为xvalues(它们是glvalues)可以出现在左侧,似乎被这个例子打破了。这里我们有一个prvalue——它不是一个glvalue——成功地出现在lhs上并接受分配。
(注意:在 POD 的情况下,上面的示例将无法编译,因此对于 POD,xvalues 和 prvalues 之间的区别似乎是有意义的。因此,这个问题专门针对非 POD 类型。)
那么,在 xvalue 和 prvalue 之间允许的使用或行为的真正区别是什么,需要将这种区别写入标准?一个不同的例子将是一个很好的替代答案。
附录
Pubby的评论是正确的。prvalue 的生命周期由编译器延长,但 xvalue 的生命周期不是。
所以,这里是这个问题的答案:
考虑以下代码:
// ***
// Answer to question, from Pubby's comment
// ***
class X
{
public:
X() : x(5) {}
int x;
};
X foo() { return X(); }
X&& goo() { return std::move(X()); } // terrible coding, but makes the point
int main()
{
foo() = X();
X&& x1 = foo(); // prvalue - lifetime extended! Object resides directly on stack as return value
X&& x2 = goo(); // xvalue - lifetime not extended. Object (possibly polymorphic) resides somewhere else.
x1.x = 6;
x2.x = 7; // Danger!
std::cout << x1.x << std::endl; // Just fine
std::cout << x2.x << std::endl; // prints garbage in VS 2012
}
这证明了纯右值和 xvalue 之间的行为差异。在这里,我们有相同的客户端代码,除了绑定的不同(prvalue 与 xvalue)。
如示例代码所示,prvalue 的生命周期会自动延长,但 xvalue 的生命周期不会。
还揭示了其他明显的差异:对于纯右值,对象本身作为函数的返回值出现在堆栈上;相应地,因为prvalue的静态类型保证是它的动态类型(见下面的答案),延长它的生命周期是有意义的,并且可以由编译器完成。
另一方面,对于 xvalue,对象位于某个未知的任意位置,因此编译器无法轻易延长其生命周期,特别是考虑到该类型可能是多态的。
感谢你的回答。