在那个函数中,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
值(因为您要返回一个副本)。这是由复制构造函数隐式完成的。所以等价于。鉴于这是一个左值,选择了复制构造函数(而不是移动构造函数)。foo
return foo;
return Foo(foo)
foo
现在,当您拥有这个新的临时值(由 构造foo
)时,来自表达式 的值本身bar(...)
就是一个纯右值。所以当你这样做时:
auto res = bar(...);
您必须Foo
从纯右值构造一个副本。由于纯右值也是右值,因此选择了带有右值引用参数的构造函数(移动构造函数)。