6

When I want a function to return me a container:

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

To be used in the following way:

vector<T> result = func(); 

In order to avoid the overhead of copying my container I often write the function so that it returns nothing but accept a non-const instance of the container.

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

To be used in the following way:

vector<T> result;
func(result); 

Is my effort meaningless because I can be sure that the compiler always uses the return value optimization?

jimifiki
  • 5,377
  • 2
  • 34
  • 60

4 Answers4

10

It is meaningless. The type of RVO you mentioned is called named RVO (NRVO), and most compilers implement it.

Regardless, in C++11, vector has move constructors, so even if NRVO didn't apply, it'd still be moved, not copied.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
2

RVO is not guaranteed but decent compilers will use it when permitted.

However the problem is RVO only helps when you are creating a new object outside the function. If you reuse the same vector by passing it by reference, you can take advantage of its reserved capacity to reduce the number of memory allocations. A local vector created inside the function will always need to allocate a new buffer internally, no matter where the return value is stored. Therefore it can be more efficient to pass the vector by reference, even though the code looks less nice.

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
2

Depends on age of your compiler. Before C++11, your alternative approach is what is needed unless the compiler supports named return value optimisation - which not all older compilers do. Also, you could also have the function return a reference to the passed vector.

From C++11, the language supports move construction, and the standard containers have working move constructors, so your first approach is fine. Purists will insist that is better. Pragmatists (who realise that not everyone can update their compilers without a huge impost) will say to pick a solution depending on whether your code needs to continue working with a mix of pre-C++11 and later compilers.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Even if using an older compiler , you could experiemtn to see if it implements copy elision. – M.M Nov 06 '15 at 09:39
  • Indeed, M.M. Or read the compiler documentation. However, there is still code that needs to work with multiple compilers (even compiler versions and settings), between operating systems, etc. – Peter Nov 06 '15 at 09:41
0

I've tried it with gcc. I realized that I cannot rely on NRVO when compiling without C++11 flags.

Since I don't like the second signature (where the function takes the container by reference) I came out with this:

Declare the function in its natural form:

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

and, when I am not sure about the compiler and the compilation flags, use it in this way:

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

in this way one gets the wanted interface and is sure to avoid elidible overheads.

Notice that the capacity of the result vector is the one of the vector returned by the function. If one wants to set the capacity for the vector, the right interface for the function is the second one.

jimifiki
  • 5,377
  • 2
  • 34
  • 60
  • Both swapping and passing a reference look ugly. You might as well stick to the reference method. – Neil Kirk Nov 07 '15 at 21:36
  • IMO swapping is better than the reference method because swapping doesn't involve the declaration. The signature of the function is much more readable (takes nothing returns a vector, in my example). – jimifiki Nov 08 '15 at 08:54
  • Reference method can take advantage of existing vector capacity. In high performance applications, sometimes you have to sacrifice some code niceness for speed. – Neil Kirk Nov 08 '15 at 16:00
  • @NeilKirk I totally agree on this point, this is why I wrote (at the end of my own answer) the last paragraph mentioning the "capacity issue" you've raised in your answer. – jimifiki Nov 08 '15 at 17:08