199

How much data is copied, when returning a std::vector in a function and how big an optimization will it be to place the std::vector in free-store (on the heap) and return a pointer instead i.e. is:

std::vector *f()
{
  std::vector *result = new std::vector();
  /*
    Insert elements into result
  */
  return result;
} 

more efficient than:

std::vector f()
{
  std::vector result;
  /*
    Insert elements into result
  */
  return result;
} 

?

7
  • 12
    How about passing the vector by reference and then filling it inside f? Commented Mar 29, 2013 at 13:47
  • 7
    RVO is a pretty basic optimization that most compiler will be capable of doing any moment. Commented Mar 29, 2013 at 13:49
  • As answers flow in, it may help you to clarify whether you are using C++03 or C++11. The best practices between the two versions vary quite a bit. Commented Mar 29, 2013 at 13:51
  • 1
    See cpp-next.com/archive/2009/08/want-speed-pass-by-value Commented Mar 29, 2013 at 13:52
  • 1
    @Morten - I didn't get your question. The way to do what I suggested is what you wrote void f(std::vector &result). Note, that this is a comment, not an answer. I just suggested one more method, you didn't mention. Commented Mar 29, 2013 at 13:56

8 Answers 8

263

In C++11, this is the preferred way:

std::vector<X> f();

That is, return by value.

With C++11, std::vector has move-semantics, which means the local vector declared in your function will be moved on return and in some cases even the move can be elided by the compiler.

Sign up to request clarification or add additional context in comments.

17 Comments

Will it be moved even without std::move?
@LeonidVolnitsky: Yes if it is local. In fact, return std::move(v); will disable move-elision even it was possible with just return v;. So the latter is preferred.
@juanchopanza: I dont think so. Before C++11, you could argue against it because the vector will not be moved; and RVO is a compiler-dependent thing! Talk about the things from 80s & 90s.
My understanding about the return value (by value) is: instead of 'been moved', the return value in the callee is created on the caller's stack, so all operations in the callee are in-place, there is nothing to move in RVO. Is that correct?
@r0ng: Yes, that is true. That is how the compilers usually implement RVO.
|
128

You should return by value.

The standard has a specific feature to improve the efficiency of returning by value. It's called "copy elision", and more specifically in this case the "named return value optimization (NRVO)".

Compilers don't have to implement it, but then again compilers don't have to implement function inlining (or perform any optimization at all). But the performance of the standard libraries can be pretty poor if compilers don't optimize, and all serious compilers implement inlining and NRVO (and other optimizations).

When NRVO is applied, there will be no copying in the following code:

std::vector<int> f() {
    std::vector<int> result;
    ... populate the vector ...
    return result;
}

std::vector<int> myvec = f();

But the user might want to do this:

std::vector<int> myvec;
... some time later ...
myvec = f();

Copy elision does not prevent a copy here because it's an assignment rather than an initialization. However, you should still return by value. In C++11, the assignment is optimized by something different, called "move semantics". In C++03, the above code does cause a copy, and although in theory an optimizer might be able to avoid it, in practice its too difficult. So instead of myvec = f(), in C++03 you should write this:

std::vector<int> myvec;
... some time later ...
f().swap(myvec);

There is another option, which is to offer a more flexible interface to the user:

template <typename OutputIterator> void f(OutputIterator it) {
    ... write elements to the iterator like this ...
    *it++ = 0;
    *it++ = 1;
}

You can then also support the existing vector-based interface on top of that:

std::vector<int> f() {
    std::vector<int> result;
    f(std::back_inserter(result));
    return result;
}

This might be less efficient than your existing code, if your existing code uses reserve() in a way more complex than just a fixed amount up front. But if your existing code basically calls push_back on the vector repeatedly, then this template-based code ought to be as good.

2 Comments

Upvoted the really best and detailed answer. However, in your swap() variant (for C++03 without NRVO) you still will have one copy-constructor copy made inside f(): from variable result to a hidden temporary object which will be at last swapped to myvec.
@JenyaKh: sure, that's a quality-of-implementation issue. The standard didn't require that the C++03 implementations implemented NRVO, just like it didn't require function inlining. The difference from function inlining, is that inlining doesn't change the semantics or your program whereas NRVO does. Portable code must work with or without NRVO. Optimised code for a particular implementation (and particular compiler flags) can seek guarantees regarding NRVO in the implementation's own documentation.
5

A common pre-C++11 idiom is to pass a reference to the object being filled.

Then there is no copying of the vector.

void f( std::vector & result )
{
  /*
    Insert elements into result
  */
} 

3 Comments

That is no more an idiom in C++11.
@Nawaz I agree. I'm not sure what the best practice is now on SO regarding questions on C++ but not specifically C++11. I suspect I should be inclined to give C++11 answers to a student, C++03 answers to someone waist-deep in production code. Do you have an opinion?
Actually, after the release of C++11 (which is 19 months old), I consider every question to be C++11 question, unless it is explicitly stated to be C++03 question.
3

It's time I post an answer about RVO, me too...

If you return an object by value, the compiler often optimizes this so it doesn't get constructed twice, since it's superfluous to construct it in the function as a temporary and then copy it. This is called return value optimization: the created object will be moved instead of being copied.

Comments

2

If the compiler supports Named Return Value Optimization (http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx), you can directly return the vector provide that there is no:

  1. Different paths returning different named objects
  2. Multiple return paths (even if the same named object is returned on all paths) with EH states introduced.
  3. The named object returned is referenced in an inline asm block.

NRVO optimizes out the redundant copy constructor and destructor calls and thus improves overall performance.

There should be no real diff in your example.

Comments

0
vector<string> getseq(char * db_file)

And if you want to print it on main() you should do it in a loop.

int main() {
     vector<string> str_vec = getseq(argv[1]);
     for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
         cout << *it << endl;
     }
}

Comments

-1

follow code will works without copy constructors:

your routine:

std::vector<unsigned char> foo()
{
    std::vector<unsigned char> v;
    v.resize(16, 0);

    return std::move(v); // move the vector
}

After, You can use foo routine for get the vector without copy itself:

std::vector<unsigned char>&& moved_v(foo()); // use move constructor

Result: moved_v size is 16 and it filled by [0]

Comments

-3
   vector<string> func1() const
   {
      vector<string> parts;
      return vector<string>(parts.begin(),parts.end()) ;
   } 

This is still efficient after c++11 onwards as complier automatically uses move instead of making a copy.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.