0

In this code, a huge vector could be (depending on compiler) copied upon return:

vector<string> foo() {
  vector<string> vec;
  //make vec huge
  return vec;
}
vector<string> bar = foo();

How do I avoid copying the vector, without relying on compiler optimizations?

Now, understand that this is just a simple example, with a single return statement. But for more complex cases, it is a possibility that even good compilers will not be able to do optimizations to avoid copying the vector. (This answer to another question mentions one such case.)

(Relying on compiler optimizations always seemed rather strange to me in the first place, since I'm supposed to be writing good, portable, C++ code, as opposed to worrying about how this and that compiler does things under the hood. All those "compiler optimization" answers to the other questions therefore puzzle me.)

One idea I came up with so far is to use C++11's smart pointers:

shared_ptr<vector<string>> foo() {
  shared_ptr<vector<string>> vec_ptr = make_shared<vector<string>>();
  //make *vec_ptr (i.e. the actual vector) huge
  return vec_ptr;
}
shared_ptr<vector<string>> bar_ptr = foo();
//now, *bar_ptr is the underlying vector we can use

It looks like this will avoid the copy in all cases. The problem is that this code is getting to be quite cumbersome.

Any good non-cumbersome alternatives? Supposedly, C++11 provides some new "move" functionality through rvalue references. Could those help?

Community
  • 1
  • 1
Dennis Ritchie
  • 1,281
  • 1
  • 10
  • 15
  • 2
    "How do I avoid copying the vector". You can either copy it, or point to it. If you don't want to copy it, you **need** to use a pointer. – parrowdice Dec 04 '12 at 13:25
  • 5
    OMG Dennis Ritchie is back from the grave *and learning C++* – R. Martinho Fernandes Dec 04 '12 at 13:40
  • 1
    Btw, if you are going to use C++11's smart pointers please pick `unique_ptr`. You should [make that your default choice](http://herbsutter.com/gotw/_103/). It is not only faster than shared_ptr, but it is also more flexible (clients can build shared_ptrs out of unique_ptrs if they want; however, if you give them shared_ptrs, they are stuck with it). I often say that shared_ptr has no overhead (if you need shared ownership with thread-safety, it has features, not overhead), but in this case it has overhead, because you don't need shared ownership. – R. Martinho Fernandes Dec 04 '12 at 13:49
  • 1
    "I'm supposed to be writing good, portable, C++ code" ...which involves writing the code in a "natural" fashion without convoluting it for optimization! This is why we rely on optimizations. Move semantics make it a bit easier to guarantee a baseline cost, but at the end of the day you need to trust your toolset. – GManNickG Dec 04 '12 at 13:53
  • 2
    @Dennis - If you can't trust your compiler to elide copying of the return value (which g++ does even at -O0), how do you know that `make_shared` doesn't take half an hour? Trying to outsmart the compiler prematurely usually doesn't pay off. – Bo Persson Dec 04 '12 at 14:02
  • 2
    Be aware that it is not normal to write C++ code that is efficient in the absence of compiler optimizations. Without inlining, standard containers and algorithms go through multiple layers of function calls to do every little thing. Quality of implementation matters in C++, you may as well take advantage of common optimizations. Just don't write code that's semantically correct only when the copy is elided. – Steve Jessop Dec 04 '12 at 14:06

2 Answers2

3

Supposedly, C++11 provides some new "move" functionality through rvalue references. Could those help?

Yes, if the compiler doesn't use RVO for any reason then it will use a move in this case as std::vector has a move operator and it's applicable when returning a single variable like this. Your original code should just do this when compiler with a c++11 compiler without any changes.

jcoder
  • 29,554
  • 19
  • 87
  • 130
  • Does this mean that all old C++ code, involving lots of potential copying (during copy initialization, parameter passing, and returns), just automagically became faster and more efficient because of C++11's move functionalities? – Dennis Ritchie Dec 04 '12 at 20:42
  • 1
    In some cases, yes absolutely it does! And you can in many cases too use std::move to indicate to the compiler that it's ok to move a value instead of copying from it. – jcoder Dec 04 '12 at 20:59
0

One old school option is to pass the vector as a reference to the function and make the changes as you need it.

void foo(vector<string> &vec){
...

foo(bar);

On the other hand, move semantics as i understand it should do exactly what you want, it just might happen without any change to your code, you can check it by stepping through with a debugger, let me read further as well.

Edit: This question seems to indicate your original code should work as a move operation automatically. This Post seems to have more information, but I am a little too sleepy to grasp everything..

Community
  • 1
  • 1
Karthik T
  • 31,456
  • 5
  • 68
  • 87