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&&
.