什么表达式创建x值

What expressions create xvalues?

本文关键字:创建 表达式 什么      更新时间:2023-10-16

我正在努力理解C++11的概念。

我说的标准草案:

xvalue(一个"eXpiring"值)也指的是一个物体,通常在其寿命即将结束时(因此资源可以被移动)。xvalue是某些类型的表达式的结果,包括右值引用(8.3.2)。[示例:调用返回类型为右值的函数的结果引用是一个x值--结束示例]

那么,产生x值的"特定类型的表达式"到底是什么呢?规范的这一部分没有详细列出这些表达式的列表。

我理解lvalue和prvalue(至少我认为,我理解)。

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

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

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

  • 转换为对对象类型的右值引用

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

  • 指向成员表达式的CCD_ 1指针,其中第一操作数是x值,第二操作数是指向数据成员的指针。

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

搜索§5的其余部分,该列表似乎是详尽无遗的。列表后面是一个示例:

struct A {
    int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);

表达式f()f().mstatic_cast<A&&>(a)a + a是x值。表达式ar是一个左值。

有两种常见的方法可以获得xvalue表达式:

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

  • 使用.*0转发右值。CCD_ 11通常用于函数模板中,以实现函数自变量的完美转发。

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

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

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

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

(在标准的技术语言中,"对象类型"并不意味着与"类类型"相同。"对象类型"包括基本类型、指针和数组,仅排除函数类型。对函数类型的右值引用始终被视为左值,而不是xvalue。)

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

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

(另一方面,数据成员查找E1->E2总是左值。)

类似地,如果E1是x值,则数据成员查找E1.*E2是x值:

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

对于各种类型的铸件:

  • dynamic_cast<Type>(expr):5.2.7第2段
  • static_cast<Type>(expr):5.2.9第1段
  • reinterpret_cast<Type>(expr):5.210第1段
  • const_cast<Type>(expr):5.211第1段
  • (Type) expr:5.4第1段

表达式是x值,当且仅当CCD_ 29是对对象类型的右值引用。Type(expr)也是如此,因为

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

(另一方面,Type{expr}总是一个prvalue。)

关于条件运算符的第5.16节最后指出,如果B和/或C是x值,则A ? B : C有时可以是x值。但完整的规则很难概括。

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

如您所述,

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++移动语义

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

一般来说,它是一个或多个级别的间接远离一个实际的右值。

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

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