Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I ensure RVO instead of copy is performed?

In many cases, I would like to create a new instance of data and return it to the API caller.

I learned that unique_ptr/shared_ptr can be used for factory pattern (for example, Factory pattern using unique_ptr in c++)

At the same time, I learned that returned value optimization (RVO) is possible in many compilers (for example, Efficient way to return a std::vector in c++).

I prefer RVO since it is easier to use the returned value without a wrapping unique_ptr and easier to read the code, however, since the RVO is not guaranteed, I don't want to sacrifice performance unexpectedly and have to use unique_ptr to ensure returned value is moved instead of copied.

Is there any approach that I can explicitly specify the return value to be moved, so that either it will not complain anything if RVO is possible or it will trigger some compiler warning if RVO is not possible? If this is possible, I can safely get rid of returning a unique_ptr in this case.

I am using C++17 and need to support Apple Clang 11.0 on macOS and g++ 9 on Linux.

Edited:

I am still learning C++ and didn't make distinction between RVO (Return Value Optimization) and NRVO (Named Return Value Optimization) when posting this question. It seems to me NRVO is more common and useful in patterns like factory method, for example:

vector<foo> vec;
// populate data into vec
return vec;

And I am looking for something like a return std::move_only(returned_value) that will give me a compiler warning if this value cannot be moved (not copy to move). Maybe I should re-phrase my question as: if NRVO is not guaranteed, why "return by value" is still the recommended way in this question (Efficient way to return a std::vector in c++), shouldn't the answer be "it depends" on your function implementation and whether or not you could accept unexpected performance cost?

like image 963
nybon Avatar asked Mar 03 '23 17:03

nybon


1 Answers

How can I ensure RVO instead of copy is performed?

The language does this already for you starting in C++17. If you have a construct like

T foo() { /*stuff*/; return T{ /*stuff*/ }; }

Then the returned object is guaranteed to be elided thanks to guaranteed copy elision.

If you have a construct like

T foo() 
{
    T obj{ /*stuff*/ }; 
    // do stuff with obj
    return obj;
}

Then you will either get NRVO (Nammed Return Value Optimization) which is not guranteed, or the compiler will move obj because there is a rule in the standard that all function local objects with automatic storage duration will be moved out of the function if they have a move constructor.

That means the only time you'll get a copy is if you are returning an object that can't be optimized (it is a named local or it's a function parameter) and it doesn't support moving. Global objects are always copied as they are not scoped to the function.

like image 154
NathanOliver Avatar answered Mar 12 '23 12:03

NathanOliver