std :: move(a).m是xvalue还是prvalue?

时间:2017-02-03 14:18:09

标签: c++11 c++14 decltype rvalue xvalue

假设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?

1 个答案:

答案 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)的含义很复杂
  

对于表达式edecltype(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&&,其中Te的类型;
   - 否则,如果e是左值,则decltype(e)T&,其中Te的类型;
   - 否则,decltype(e)e的类型。

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

如果你想正确地对它进行分类,可以强制用一组额外的括号忽略该子弹(显然):decltype((std::move(a).m))会给你M&&