18

什么是 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,对象位于某个未知的任意位置,因此编译器无法轻易延长其生命周期,特别是考虑到该类型可能是多态的。

感谢你的回答。

4

2 回答 2

7

对于多态非 pod 类型的 xvalue 表达式,表达式的动态类型在编译时通常是未知的(因此它们上的 typeid 表达式被求值,并且虚拟函数调用通常不能被去虚拟化)。

对于纯右值,这不适用。动态类型等于静态类型。

另一个区别是decltype(e)xvalues 的 rvalue 引用类型和 prvalues 的非引用类型。

另一个区别是,对纯右值没有进行左值到右值的转换(它们已经是结果会产生的)。这可以通过一些相当奇怪的代码来观察

struct A { 
    int makeItANonPod; 
    A() = default;

  private:
    int andNonStdLayout;
    A(A const&) = default;
};

void f(...);

int main() {
  f(A()); // OK
  f((A&&)A()); // illformed
}
于 2013-03-18T17:44:41.443 回答
0

xvalue 和 prvalue 之间的真正区别是什么?xvalue 是一种右值,它可以是 cv 限定的,可以引用一个对象,并且动态类型等于或不等于静态类型。

const int&& foo();
int&& _v=foo();

如果没有 xvalue,上述函数 foo 的返回值只能是右值。但是内置类型没有 const rvalue!因此,上面的非常量变量 _v 总是可以绑定 foo() 的返回值,即使我们希望 foo() 返回一个 const 右值。

于 2014-07-29T06:46:40.973 回答