假设m
是非引用类型(T
)的非静态数据成员。根据{{3}},std::move(a).m
是一个prvalue直到c ++ 11。我猜它应该是c ++ 11之后的xvalue。如果我错了,请纠正我。
但是{+ 1}}在c ++ 14(visual studio,clang,gcc)中仍然是decltype(std::move(a).m)
(不是T
),这表明T&&
仍然是一个prvalue 。那么std::move(a).m
是xvalue还是prvalue?
答案 0 :(得分:2)
std::move(a).m
是一个xvalue。
新的措辞在[basic.lval]中更加清晰:
- prvalue 是一个表达式,其评估初始化对象或位字段,或计算运算符的操作数的值,由其出现的上下文指定。
- xvalue 是一个glvalue,表示可以重用资源的对象或位字段(通常因为它接近其生命周期的末尾)。
根据这些定义,std::move(a).m
是一个x值而不是一个prvalue,因为它表示一个对象。
我最好考虑这个问题的方法是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 类型。从[dcl.type.simple]:
decltype(e)
的含义很复杂
对于表达式
e
,decltype(e)
表示的类型定义如下:
- 如果e
是未明确的 id-expression ,命名从分解声明的 identifier-list 引入的左值或引用,decltype(e)
是参考类型在规范中给出 分解声明(8.5);
- 否则,如果e
是未加密码的 id-expression 或未加括号的类成员访问权限(5.2.5),decltype(e)
是e
命名的实体的类型。如果没有这样的实体,或者e
命名一组重载函数,则程序格式不正确;
- 否则,如果e
是xvalue,则decltype(e)
为T&&
,其中T
为e
的类型;
- 否则,如果e
是左值,则decltype(e)
为T&
,其中T
为e
的类型;
- 否则,decltype(e)
是e
的类型。
在这种情况下,我们有一个类成员访问权限,因此您只需获得m
的类型 - M
而不是M&&
。在某种程度上,这是有道理的,您要求声明的m
类型,并且您获得了m
的声明类型。
如果你想正确地对它进行分类,可以强制用一组额外的括号忽略该子弹(显然):decltype((std::move(a).m))
会给你M&&
。