请查看以下代码:
class MyClass{
public:
MyClass(){}
MyClass(MyClass &&){}
MyClass(const MyClass &){}
};
MyClass f1(){
MyClass &&o=MyClass();
/*...*/
return std::move(o);//or return static_cast<MyClass &&>(o);
}
MyClass f2(){
MyClass o=MyClass();
/*...*/
return o;
}
int main(int, char **){
auto a=f1();
auto b=f2();
}
函数f2
是返回对象的常规形式。 NRVO可能适用,并且可以避免额外的复制构造函数调用。 f1
是使用右值引用的新表单。对于不支持NRVO但支持右值引用的系统,调用移动构造函数而不是复制构造函数,在大多数情况下这被认为是更好的。
f1
的问题在于:在这种情况下是否有任何支持NRVO的编译器?毕竟,它似乎是未来更好的形式。
答案 0 :(得分:4)
在这种情况下是否有NRVO的编译器支持?
定义“编译器支持”?
f1
所做的是完全破坏编译器优化MyClass
副本的能力。让我们详细看一下f1
MyClass &&o=MyClass();
这会创建临时,而不是堆栈变量。然后,临时绑定到名为o
的r值引用,它将临时的生命周期延长到函数的末尾。
return std::move(o); //or return static_cast<MyClass &&>(o);
这将返回对临时堆栈绑定的r值引用的r值引用。因为你要返回一个值而不是一个引用,所以编译器必须从中创建一个临时值。
将省略临时复制/移动到a
。但是你仍然创建了两个临时值(原始值和返回值)。
所以f1
执行以下操作:
create temporary
copy/move from temporary to return value
elide copy/move from return value to `a`.
f2
:
create stack variable
elide copy/move from stack variable to `b`.
如果NVRO不存在,您有:
create stack variable
copy/move from stack variable to return value
elide copy/move from stack variable to `b`.
因此,f2
最差等于f1
。而且更有可能,更好。
请停止尝试超越编译器。只是让复制精灵完成它的工作。
答案 1 :(得分:2)
这是当前编译器(MSVC10 / gcc trunk)的工作原理:
假设MyClass是可移动的
f1 : move
f2 :
worst case : move
best case : NRVO
假设MyClass不可移动:
f1 : copy
f2 :
worst case : copy
best case : NRVO
因此,即使编译器变得更好并开始为f1之类的函数执行NRVO,当f2经典C ++ 03函数已经达到最优时,为什么还要使代码复杂化呢?