Which function structure is better?

228 views Asked by At

Look at the following code:

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();
}

Function f2 is the normal form of returning an object. NRVO may apply and the additional copy constructor call could be avoid. f1 is the new form that uses rvalue reference. For systems that do not support NRVO but support rvalue reference, the move constructor is called rather than copy constructor, which would be considered better in most of the cases.

The problem of f1 is that: are there any compilers that support of NRVO in this case? It seems to be the better form in the future after all.

2

There are 2 answers

0
Arzar On BEST ANSWER

This is how current compilers (MSVC10 / gcc trunk) works :
Assuming MyClass is moveable

f1 : move   
f2 :   
worst case : move 
best case : NRVO 

Assuming MyClass is not moveable :

f1 : copy    
f2 :    
worst case : copy    
best case : NRVO 

So even if compilers get better and start doing NRVO for functions like f1, why bother complicate the code when f2 classical C++03 functions are already optimal ?

2
Nicol Bolas On

are there any compiler support of NRVO in this case?

Define "compiler support"?

What f1 does is completely destroy the compiler's ability to optimize the copy of MyClass. Let's look at f1 in detail

MyClass &&o=MyClass();

This creates a temporary, not a stack variable. Then that temporary is bound to an r-value reference called o, which extends the lifetime of the temporary to the end of the function.

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

This returns an r-value reference of a stack-bound r-value reference to a temporary. And since you're returning a value, not a reference, the compiler has to create a temporary from it.

The copy/move of the temporary into a will be elided. But you still created two temporaries (the original and the return value).

So f1 does the following:

create temporary
copy/move from temporary to return value
elide copy/move from return value to `a`.

f2 does:

create stack variable
elide copy/move from stack variable to `b`.

If NVRO doesn't exist, you have:

create stack variable
copy/move from stack variable to return value
elide copy/move from stack variable to `b`.

So, f2 is at worst equal to f1. And more than likely, better.

Please stop trying to out-think the compiler. Just let copy elision do its job.