【问题标题】:What expressions create xvalues?什么表达式创建 xvalues?
【发布时间】:2012-07-20 15:01:47
【问题描述】:

我正在尝试理解 C++11 的概念。

我所说的标准草案:

一个 xvalue(一个“eXpiring”值)也指一个对象,通常接近其生命周期的末尾(因此它的 例如,可以移动资源)。 xvalue 是某些类型的表达式的结果,涉及 右值引用 (8.3.2)。 [ 示例:调用返回类型为右值的函数的结果 参考是一个xvalue。 ——结束示例]

好的,那么产生 xvalues 的“某些类型的表达式”到底是什么?这部分规范没有详细列出这些表达式。

我了解左值和纯右值(至少我认为,我了解)。

【问题讨论】:

  • 标准的那部分解释了对该术语的一般理解。它没有详细说明生成 xvalue 的每种表达式。这发生在标准的其他地方。
  • @NicolBolas :我之前已经阅读过该问题和答案,因此我决定发布另一个问题正是由于这个原因。
  • @PermanentGuest:所以你的问题是生成 xvalue 的表达式是什么。您应该编辑您的问题以更清楚地表明您正在寻找 xvalues 的来源,并对其进行编辑以让人们知道您已阅读该问题,但它特别不会很清楚。
  • @NicolBolas:感谢您的编辑。抱歉,周末无法回复您。

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


【解决方案1】:

在§5 (C++11 §5[expr]/6) 的介绍中有一个有用的非规范性注释:

[ 注意:表达式是一个 xvalue 如果它是:

  • 调用函数的结果,无论是隐式还是显式,其返回类型是对对象类型的右值引用,

  • 对对象类型的右值引用的强制转换,

  • 一个类成员访问表达式,指定一个非引用类型的非静态数据成员,其中对象表达式是一个 xvalue,或

  • .* 指向成员的表达式,其中第一个操作数是 xvalue,第二个操作数是指向数据成员的指针。

一般来说,这条规则的效果是命名的右值引用被视为左值,而对对象的未命名的右值引用被视为xvalue;对函数的右值引用被视为左值,无论是否命名。 ——尾注]

搜索 §5 的其余部分,这个列表似乎很详尽。列表后面是一个例子:

struct A {
    int m;
};

A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);

表达式 f()f().mstatic_cast&lt;A&amp;&amp;&gt;(a)a + a 是 xvalues。表达式ar 是一个左值。

获取 xvalue 表达式的常用方法有两种:

  • 使用std::move 移动对象。 std::move 对右值引用类型执行static_cast 并返回右值引用。

  • 使用std::forward 转发右值。 std::forward 通常用于函数模板中,以实现函数参数的完美转发。

    如果提供给函数模板的参数是右值,则参数类型将是右值引用,即左值。在这种情况下,std::forward 对右值引用类型执行 static_cast 并返回右值引用。

    (注意:如果提供给函数模板的参数是左值,则参数类型将是左值引用,std::forward 将返回左值引用。)

【讨论】:

  • 嘘,就像我正在建立我的示例列表并偶然发现了这句话一样。 :( +1,我回答完后应该完成标签编辑。:)
  • 感谢您提供详细列表...这与其他答案和许多其他谷歌搜索相结合,在某种程度上消除了对基本定义的混淆。
  • 非常有用的答案,但有一点我仍然无法理解。我知道在Foo bar(){Foo foo; return foo;} 中,表达式foo 是return 语句中的一个xvalue(如果错误请更正)。我没有看到你引用的列表涵盖了它。
  • @ricab 我认为 foo 将是一个prvalue。 xvalue 始终是右值引用类型的东西,可以分配给右值引用。这是我目前的理解。
  • 我的意思是 foo 将是一个左值。再想一想,xvalue 是一个右值,它可以被分配一些身份。并且只有可以具有标识的右值基本上是 && 类型,当我们显式转换它们时:std::move 或从函数返回。所以表达式 foo 是一个左值,但函数调用 bar() 是一个 xvalue。您可以将 bar() 分配给 Foo&& 并给它一个身份。
【解决方案2】:

第 5 章描述了有效表达式的语法,为每个表达式语法列出了表达式为左值、xvalue 或 prvalue 的条件。第 5 条中可能的 xvalue 的完整列表是:

5.2.2 第 10 段:如果结果类型是对对象类型的右值引用,则函数调用是...一个 xvalue。

(在标准的技术语言中,“对象类型”与“类类型”的含义不同。“对象类型”包括基本类型、指针和数组,仅不包括函数类型。右值引用to 函数类型始终被视为左值,而不是 xvalue。)

最值得注意的返回右值引用的函数当然是std::move,有时是std::forward

5.2.5 第 4 段:如果 E2 是非静态数据成员...如果 E1 是 xvalue,则 E1.E2 是 xvalue

(另一方面,数据成员查找E1-&gt;E2 始终是左值。)

同样,如果E1 是一个xvalue,那么数据成员查找E1.*E2 是一个xvalue:

5.5 第 6 段:.* 表达式的结果,其第二个操作数是指向数据成员的指针,其值类别 (3.10) 与其第一个操作数相同。

对于各种类型的演员表:

  • dynamic_cast&lt;Type&gt;(expr):5.2.7第2段
  • static_cast&lt;Type&gt;(expr): 5.2.9 第 1 段
  • reinterpret_cast&lt;Type&gt;(expr):5.2.10第1段
  • const_cast&lt;Type&gt;(expr):5.2.11第1段
  • (Type) expr: 5.4 第 1 段

当且仅当Type 是对对象类型的右值引用时,表达式才是一个xvalue。 Type(expr) 也是如此,因为

5.2.3 第 1 段:如果表达式列表 [在类型名称后面的括号中] 是单个表达式,则类型转换表达式等效于(在定义上,如果在含义上定义)对应的强制转换表达式 (5.4) .

(另一方面,Type{expr} 始终是纯右值。)

关于条件运算符的第 5.16 节最后说,如果 B 和/或 C 是​​一个 xvalue,A ? B : C 有时可以是一个 xvalue。但完整的规则很难总结。

如果表达式最终调用用户定义的重载运算符函数,则第 5.2.2 节适用于该表达式,而不是描述内置运算符行为的表达式。 (参见@James 发布的示例中的表达式a + a。)

【讨论】:

    【解决方案3】:

    正如你所说,

    一个xvalue(一个“eXpiring”值)也指一个对象,通常接近其生命周期的末尾(例如,它的资源可能会被移动)。

    关注:objectbe movedend lifetime

    这是一个例子:

    void move_test(){
        std::string s = "I'm here!";
        std::string m  =  std::move(s);  // move from <s> to <m>
        // s is now in an undefined, but valid state; 
        std::cout << "s=" << s << "; &s=" << &s << std::endl;
    }
    

    通过移动,我们可以将命名值(左值,这里是s)转换为右值(即std::move(s))。更具体地说,因为它不能是prvalue(它有一个名字!换句话说,它有一个身份),它最终是一个xvalue

    xvalue 始终服务于C++ 移动语义

    【讨论】:

      【解决方案4】:

      从我所读到的内容中,我了解到将某事物称为 xvalue 是一种奇特的说法:

      xvalue 只是一个右值,其存储空间可能已被释放,因此使用它意味着您必须自己验证它的存在。

      通常,它是远离实际右值的一个或多个间接级别。

      相比之下,只要在作用域内,右值就保证其存储空间存在。

      我可能错了,但这是我所理解的。

      【讨论】:

      • 不,xvalue 是一个仍然有效但即将被丢弃的值。这通常意味着可以安全地从其中“窃取”资源并改写其现有值。
      • @aschepler:例如[]() { int x = 5; return std::move(x); } 的返回类型不是 xvalue 吗?如果不是,那是什么?
      • 类型没有值类别;表达式。
      • @aschepler:哦,好吧,那([]() { int x = 5; return std::move(x); })() 怎么样?
      • 一个没有尾随返回类型且包含多个语句的 lambda 表达式具有隐式尾随返回类型void。如果将声明移到 lambda 之外,则隐式尾随返回类型将为 int,因为在必须推导返回类型时应用了左值到右值的转换。如果您将其设为[]()-&gt; int&amp;&amp; {...},那么该表达式当然是xvalue,因为它是对虚构结构的operator() 的函数调用。但是没有办法检测到它是一个悬空引用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      相关资源
      最近更新 更多