value 返回的右值引用参数是 xvalue 吗?

Is a rvalue reference parameter that is returned by value an xvalue?

我的理解是,在下面的函数中,语句return foo;中的表达式foo是一个xvalue,因为它表示的对象即将过期(即使foo是前面语句中的左值):

Foo bar()
{
    Foo foo;
    change(foo);
    return foo;
}

What expressions create xvalues? 不包含这样的过期值。

在以下情况下会发生变化吗?

Foo bar(Foo&& foo)
{
    change(foo);
    return foo;
}

foo 是 return 语句中的一个 xvalue 吗?特别是,它是移动的候选者吗?而对于 RVO?或者应该使用 return std::move(foo)?

我不知道在第一种情况的 return 语句中将表达式 foo 分类为 xvalue 的正式规则是什么,所以我无法在第二种情况下对其进行测试。

在该函数中,foo 是类型为“对 Foo 的右值引用”的 lvalue。当你 return 它时,由于必须构造一个副本(由于 returned 值的类型),你正在构造一个全新的值,这使得 bar(...) 成为 prvalue,根据 §3.10.1.5:

A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is also a prvalue. — end example ]

由于在函数内部,foo 是一个 左值 ,表达式 return foo 不是移动构造的候选对象,并且the copy constructor is selected.

是的,RVO 在这里适用,假设没有先选择一步棋。这里在这方面没有什么特别之处。根据 §12.8.31:

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

[...]


澄清一下,foo 本身是一个左值,但是声明:

return foo;

最终(从 bar(...) 表达式)产生纯右值,因为给定 return 类型,表达式等同于:

return Foo(foo);

这意味着从 foo 复制的临时值是 return 从函数 bar 编辑的。


Rereading the reply, it still does not make sense to me. You say Due to the fact that, inside the function, foo is an lvalue, the expression return foo is not a candidate for a move construction and the copy constructor is selected. Why is this true in one case and not the other?

当 returning foo 时,您必须从 lvalue 创建一个新的 Foo 值(因为您正在 returning 副本) 参考 foo。这是由复制构造函数隐式完成的。所以 return foo; 等价于 return Foo(foo)。鉴于 foo 是一个左值,复制构造函数被选中(而不是移动构造函数)。

现在,当您有了这个新的临时值(从 foo 构造)时,表达式 bar(...) 产生的值本身就是纯右值。所以当你这样做时:

auto res = bar(...);

您必须从纯右值构造一个 Foo 副本。由于纯右值也是右值,因此选择具有右值引用参数的构造函数(移动构造函数)。