我的理解是,在以下函数中,语句foo
中的表达式return foo;
是一个xvalue,因为它表示的对象即将到期(即使foo
是一个左值在以前的陈述中):
Foo bar()
{
Foo foo;
change(foo);
return foo;
}
What expressions create xvalues?未涵盖此类到期值。
在以下情况下会改变吗?
Foo bar(Foo&& foo)
{
change(foo);
return foo;
}
返回语句中foo
是否为x值?特别是,它是移动的候选人吗?对于RVO?或者应该使用return std::move(foo)
?
我不知道在第一种情况的return语句中将表达式foo
分类为xvalue的正式规则是什么,所以我不能在第二种情况下测试它。
答案 0 :(得分:2)
在该函数中,foo
是左值,类型为“Foo
的右值引用”。当你返回它时,由于必须构造一个副本(由于返回值的类型),你构建一个全新的值,使bar(...)
成为 prvalue ,按照§3.10.1.5:
prvalue(“纯”rvalue)是一个不是xvalue的rvalue。 [示例:调用返回类型不是引用的函数的结果是prvalue。诸如12,7.3e5或true之类的文字的值也是prvalue。 - 结束例子]
由于在函数内foo
是左值,因此表达式return foo
不是移动构造的候选者the copy constructor is selected
是的,RVO适用于此处,假设未先选择移动。这方面没有什么特别之处。根据§12.8.31:
在下列情况下(可以合并以消除多份副本),允许复制/移动操作(称为复制省略)的省略:
- 在具有类返回类型的函数的return语句中,当表达式是具有与函数相同的cv-非限定类型的非易失性自动对象(函数或catch子句参数除外)的名称时通过将自动对象直接构造到函数的返回值
中,可以省略复制/移动操作[...]
为了澄清,foo
本身是一个左值,但声明:
return foo;
最终结果(来自bar(...)
表达式)在prvalue中,因为给定返回类型的事实,表达式相当于:
return Foo(foo);
表示从foo
函数返回从bar
复制的临时值。
重读回复,对我来说仍然没有意义。你说因为在函数内部,foo是一个左值,表达式return foo不是移动构造的候选者,并且选择了复制构造函数。为什么在一个案例中这是真的而不是另一个案例?
返回foo
时,您必须从左值引用Foo
创建新的foo
值(因为您要返回副本)。这是由复制构造函数隐式完成的。因此return foo;
相当于return Foo(foo)
。鉴于foo
是左值,选择了复制构造函数(而不是移动构造函数)。
现在,当你有了这个新的临时值(由foo
构造)时,表达式bar(...)
中出现的值本身就是一个prvalue。所以当你这样做时:
auto res = bar(...);
你必须从prvalue构造一个Foo
副本。由于prvalue也是一个rvalue,因此选择了带有rvalue引用参数(move constructor)的构造函数。