Return value optimization: ho can I avoid creating a copy of the huge STL containers.

When I want a function to return a container to me:

vector<T> func(){ vector<T> result; ... return result; } 

Used as follows:

 vector<T> result = func(); 

To avoid the overhead of copying my container, I often write a function so that it returns nothing but accepts a non-constant instance of the container.

 void func(vector<T>& result){ result.clear(); ... result; } 

Used as follows:

 vector<T> result; func(result); 

My effort is pointless because I can be sure that the compiler always uses return value optimization?

+5
source share
4 answers

It's pointless. The type of RVO mentioned is called RVO (NRVO), and most compilers implement it.

Regardless, in C ++ 11, vector has move constructors, so even if NRVO has not been used, it is still moved, not copied.

+10
source

RVO is not guaranteed, but decent compilers will use it if allowed.

However, the problem is that RVO only helps when creating a new object outside the function. If you reuse the same vector by passing it by reference, you can use its reserved capacity to reduce the number of memory allocations. A local vector created inside a function will always need to be allocated a new buffer inside, regardless of where the return value is stored. Therefore, it is more efficient to pass a vector by reference, although the code looks less pleasant.

+2
source

Depends on the age of your compiler. Before C ++ 11, your alternative approach is what is needed if the compiler does not support named optimization of the return value - which not all older compilers do. In addition, you can also return a link to the passed vector function.

From C ++ 11, the language supports moving constructs, and standard containers have working moving mechanisms, so your first approach is fine. Purists will insist on what is best. Pragmatists (who understand that not everyone can upgrade their compilers without a huge impost) will say to choose a solution depending on whether your code should continue to work with a combination of pre-C ++ 11 and later compilers.

+2
source

I tried this with gcc. I realized that I can not rely on NRVO when compiling without C ++ 11 flags.

Since I do not like the second signature (where the function accepts the container by reference), I got the following:

Declare a function in its natural form:

 vector<T> func(){ vector<T> result; ... return result; } 

and when I'm not sure about the compiler and compilation flags, use it like this:

 vector<T> result; func().swap(result) 

Thus, you get the required interface and be sure to avoid flexible overhead.

Note that the capacity of the result vector is the vector returned by the function. If you want to set the capacity for the vector, the right interface for the function is second.

0
source

Source: https://habr.com/ru/post/1235349/


All Articles