1

看下面的代码:

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 的编译器?毕竟,这似乎是未来更好的形式。

4

2 回答 2

4

在这种情况下是否有 NRVO 的编译器支持?

定义“编译器支持”?

这样做f1完全破坏编译器优化MyClass. f1我们来详细看看

MyClass &&o=MyClass();

这会创建一个临时变量,而不是堆栈变量。然后该临时值绑定到一个名为 的 r 值引用o,这会将临时值的生命周期延长到函数的末尾。

return std::move(o); //or return static_cast<MyClass &&>(o);

这将返回堆栈绑定 r-value 引用的 r-value 引用到临时。而且由于您返回的是一个值,而不是一个引用,编译器必须从中创建一个临时值。

临时进入的复制/移动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

请停止试图超越编译器。让复制省略来完成它的工作。

于 2011-10-26T04:02:03.073 回答
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 函数已经是最优时,为什么还要让代码复杂化呢?

于 2011-10-26T09:54:27.567 回答