【问题标题】:What is an example of a difference in allowed usage or behavior between an xvalue and a prvalue FOR NON-POD objects?什么是非 POD 对象的 xvalue 和 prvalue 之间允许的使用或行为差异的示例?
【发布时间】:2013-03-07 02:59:59
【问题描述】:

What are rvalues, lvalues, xvalues, glvalues, and 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)可以出现在左侧,这个例子似乎被打破了。这里我们有一个纯右值——它不是一个左值——成功地出现在 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 vs. xvalue)之外,这里我们有相同的客户端代码。

如示例代码所示,prvalue 的生命周期会自动延长,但 xvalue 的生命周期不会。

还揭示了其他明显的区别:对于纯右值,对象本身作为函数的返回值出现在堆栈上;相应地,因为prvalue的静态类型保证是它的动态类型(见下面的答案),延长它的生命周期是有意义的,可以由编译器来完成。

另一方面,对于 xvalue,对象位于某个未知的任意位置,因此编译器无法轻易延长其生命周期,尤其是考虑到该类型可能是多态的。

感谢您的回答。

【问题讨论】:

  • AFAIK,如果foo() 是右值,X&amp;&amp; x = foo() 会创建一个临时值,但如果foo() 是一个xvalue,则不会。也许这有区别? (虽然很有可能我错了)
  • 作业的左侧并不是一个好的规则。例如const&amp; 是一个左值,不能出现在 lhs 中,而 class 类型的右值可以出现在 lhs...
  • 那个类一个pod..
  • @Pubby 太棒了!您的评论是正确答案。请看我的附录。如果你把它写下来作为答案,我将能够奖励它。
  • 一个更好的例子是X&amp;&amp; x2 = std::move( X() );,它创建了一个在x2被初始化时仍然存在的纯右值,但是因为临时值变成了一个xvalue,它的生命周期没有延长它死在完整表达式的结尾。

标签: c++ c++11 rvalue


【解决方案1】:

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

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

另一个区别是decltype(e) 是 xvalues 的右值引用类型和 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
}

【讨论】:

  • 这是一个很好的答案。但是请注意,答案表明编译器如何看待事物 - 它并不表示使用或行为差异的示例(可能除了纯右值情况下的效率差异)。
  • 另请注意,Pubby 的评论(上图)是一个很好的例子,说明了 xvalue 和 prvalue 之间的明显行为区别;我已附加我的答案以包含示例代码。
  • @dan 好吧,我没有把它作为一个例子,因为它是一个当前为它打开缺陷报告的例子。此外,还有一个缺陷报告会使 T().foo 成为一个 xvalue(如果 foo 是一个对象成员)。绑定对它的引用也会导致周围 T 对象的生命周期延长,所有这些都符合当前规则。
  • @dan 没有。是否评估 typeid 是语言差异。表达式可能会修改对象或具有未定义的行为或在评估时抛出异常。
  • 缺陷报告是关于 xvalue 的生命周期延长,prvalue 的生命周期没有延长,还是别的什么?
【解决方案2】:

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

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

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

【讨论】:

  • prvalues和xvalues的类别是不相交的,所以xvalue不是一种prvalue。也许您的意思是“xvalue 是一种右值……”?
猜你喜欢
  • 2012-12-09
  • 2021-01-31
  • 1970-01-01
  • 2013-06-22
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多