3

假设是非引用类型 ( )m的非静态数据成员。T根据 cppreferencestd::move(a).m在 c++11 之前是纯右值。我想它应该是 c++11 之后的 xvalue。如果我错了,请纠正我。

但是在 c++14(visual studio、clang、gcc)中decltype(std::move(a).m)仍然是T(不是T&&),这表明std::move(a).m它仍然是一个纯右值。那么是std::move(a).mxvalue还是prvalue?

4

1 回答 1

2

std::move(a).m是一个 xvalue。

在 [basic.lval] 中,新的措辞使这一点更加清晰:

  • prvalue是一个表达式,它的求值初始化一个对象或一个位域,或者计算一个运算符的操作数的值,由它出现的上下文指定。
  • 一个xvalue是一个glvalue,它表示一个对象或位域,其资源可以被重用(通常是因为它接近其生命周期的终点)。

根据这些定义,std::move(a).m是一个 xvalue 而不是一个纯右值,因为它表示一个对象。

我发现最好的考虑方式是glvalues具有标识并且rvalues可以安全移动-lvalues具有标识并且不安全移动,xvalues具有标识并且可以安全移动,prvalues没有标识并且可以安全地离开。这种分类法使这类问题更容易推理。

此外,[expr] 中有一条注释,更具体:

[注意:一个表达式是一个 xvalue,如果它是: [...]
— 转换为对对象类型的右值引用,
— 类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是一个 xvalue,或 [...] —
尾注]

std::move(a)是对右值引用的强制转换,xvalue 也是。std::move(a).m是 xvalue 的类成员访问,xvalue 也是。


至于decltype(std::move(a).m)。请注意,这个词本身来自decl ared type。什么decltype(e)意思的规则很复杂,来自[dcl.type.simple]:

对于表达式e,由 表示的类型decltype(e)定义如下:
— 如果e是一个无括号的id 表达式,命名从分解声明的标识符列表中引入的左值或引用,是分解声明decltype(e)规范中给出的引用类型(8.5);
否则,如果e是无括号的id 表达式 或无括号的类成员访问 (5.2.5), decltype(e)则为由 命名的实体的类型e。如果没有这样的实体,或者如果e命名了一组重载函数,则程序是非良构的;
— 否则,如果e是一个 xvalue,decltype(e)T&&,T的类型在哪里e;
— 否则,如果e是左值,decltype(e)T&,其中T的类型是e;
— 否则,decltype(e)是 的类型e

在这种情况下,我们有一个类成员访问权限,因此您只需获得m- 的类型,即 isM和 not M&&。在某种程度上,这是有道理的,您要求声明的类型为 ,m而您获得了声明的类型为m

如果您想对其进行正确分类,您可以使用一组额外的括号(显然)强制忽略该项目符号:decltype((std::move(a).m))会给您M&&.

于 2017-02-03T15:24:03.767 回答