2

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

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

哪些表达式会创建xvalues .

在以下情况下会改变吗?

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

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

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

4

1 回答 1

2

在那个函数中,foo是一个“rvalue reference to”类型的Foo值。当你返回它时,由于必须构造一个副本(由于返回值的类型),你正在构造一个全新的值,它根据§3.10.1.5 生成bar(...)一个prvalue :

prvalue(“纯”右值)是不是 xvalue 的右值。[ 示例:调用返回类型不是引用的函数的结果是纯右值。诸如 12、7.3e5 或 true 之类的文字的值也是纯右值。—结束示例]

由于在函数内部,foo是一个左值,因此表达式return foo不是移动构造的候选对象,因此选择了复制构造函数

是的,RVO 适用于此,假设未先选择移动。在这方面没有什么特别之处。根据§12.8.31:

这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本):

  • 在具有类返回类型的函数的 return 语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

[...]


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

return foo;

由于bar(...)给定返回类型,表达式等价于:

return Foo(foo);

这意味着foo从函数返回 一个临时值,从中复制bar


重读回复,对我来说仍然没有意义。你说由于在函数内部, foo 是一个左值,表达式 return foo 不是移动构造的候选者,因此选择了复制构造函数。为什么这在一种情况下是正确的,而在另一种情况下则不然?

返回时,您必须从左值引用foo创建一个新Foo值(因为您要返回一个副本)。这是由复制构造函数隐式完成的。所以等价于。鉴于这是一个左值,选择了复制构造函数(而不是移动构造函数)。fooreturn foo;return Foo(foo)foo

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

auto res = bar(...);

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

于 2015-07-20T15:34:48.447 回答