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

Is std::move(a).m an xvalue or a prvalue?

假设 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).m 是一个 xvalue 还是一个 prvalue?

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

[basic.lval]:

中的新措辞使这一点更加清晰
  • A prvalue is an expression whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator, as specified by the context in which it appears.
  • An xvalue is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime).

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

我认为最好的思考方式是左值具有身份并且右值可以安全地移动 - 左值具有身份并且不能安全地离开,xvalues 具有身份并且可以安全地离开,并且prvalues 没有身份并且可以安全地移动。这种分类法使这类问题更容易推理。

另外[expr]里有个注释,比较具体:

[ Note: An expression is an xvalue if it is: [...]
— a cast to an rvalue reference to object type,
— a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or [...]
—end note ]

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


至于decltype(std::move(a).m)。请注意,该词本身来自 declared typedecltype(e) 含义的规则很复杂,来自 [dcl.type.simple]:

For an expression e, the type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression naming an lvalue or reference introduced from the identifier-list of a decomposition declaration, decltype(e) is the referenced type as given in the specification of the decomposition declaration (8.5);
otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
— otherwise, decltype(e) is the type of e.

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

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