1

Let's say I have two functions. One returns std::vector<int> and another returns a class that has a std::vector<int> member.

// using only the vector
std::vector<int> buildVector(){
     std::vector<int> value;
     // build the vector...
     return value;
};

// using and returning a class instead
struct Something{
    std::vector<int> m_vector;
}
Something buildSomething(){
     Something value;
     // build the object...
     return value;
};

Will the two functions buildVector() and buildSomething()perform shallow copies of the objects they're returning (avoiding an unnecessary heap allocation)? Or will they not? Or would they behave differently?

I assume the internal pointers of the vector will remain valid, unless I'm mistaken and the local variable decides to deallocate this memory. (will they?)

I'm not after performance per se, but I don't want to perform allocations that are not needed. And writing the functions in this form feels the most natural for what I'm doing.

My compiler doesn't use C++11; would this be a different scenario in the current standard compared with the prior standard?

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
  • 1
    Possible duplicate: http://stackoverflow.com/q/12953127/596781 – Kerrek SB Jul 31 '14 at 16:30
  • @KerrekSB - that question and answer linked seems to have a far broader scope, and doesn't touch on allocation or deallocation for the local value, I'm not sure how to apply them here I'm afraid... – Anne Quinn Jul 31 '14 at 16:46
  • The short answer is that Everything Is Fine. Write readable code and don't worry about micro optimizations. – Kerrek SB Jul 31 '14 at 16:49

2 Answers2

4

Short answer: Before C++11, it would do a copy of every element. It would allocate a buffer on the side and do a memcpy or the like. Very fast, very efficient, as vector stores contiguous memory. Pointers to a stack vector that's returned by value would, probably, not be valid anymore.

In C++11, the Std lib folks can use move operations to effectively "steal" the guts from one vector for another, resulting in just a couple pointer swaps. This is implementation specific, so while your compiler may be C++11, the Stdlib may not be up to date. C++14 makes this even more transparent and awesome, but again, your platforms implemenation of the Stdlib may not be updated to take advantage of Move semantics.

As a side note, The Rule of Three has become the Rule of Five. See Rule-of-Three becomes Rule-of-Five with C++11?

I'm hoping to find out if the language folks can do something about that...some compiler magic to make it the rule of three again to prevent code-bloat.

Community
  • 1
  • 1
Caladain
  • 4,801
  • 22
  • 24
  • The "compiler magic" already exists: the rules for implicit special operator generation. If you use the [rule of zero](http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html) you never need to write any of those five special functions. – Mankarse Aug 01 '14 at 01:49
  • Strongly disagree on the word "never". Most of the time, you should be fine, but it only holds true if all your resources are held in shared_ptr's, you don't need to do any signal processing/cleanup/setup in constructor or destructor, or a hundred other cases. – Caladain Aug 01 '14 at 01:52
  • :-) That said, i've made it a goal to replace older code with RAII as much as possible. It's just not doable 100% in my current code base (inherited) – Caladain Aug 01 '14 at 01:55
1

Firstly: in all cases, buildVector and buildSomething are totally equivalent in terms of the copies/moves or whatever of the data (unless your compiler has a braindead optimiser).

Prior to C++11, you were at the mercy of your compiler. The code has the semantics of "allocate the memory required for value, allocate new memory for the return value and copy data into that memory, and (if it occurs) copy the return value into the variable in the calling code (again, allocating new memory and copying every element)". This seems bad, but one or both of the two copies may be elided, so on (most, or perhaps all) production quality compilers, the code is more likely to operate as "allocate the memory required for value... and suddenly make the variable in the calling code be exactly the same object as value" (no (C++ visible) copies, moves or anything needed whatsoever).

With C++11, anywhere where "copy elision" may take place, must call the move constructor as a fallback (further falling back to a copy constructor if no move constructor is available, for compatibility). The move constructor for std::vector (in a C++11 conformant implementation) works by 'stealing' the memory from its operand, creating a new std::vector with the same value as the one being moved from and leaving the moved from vector in a valid but unspecified state. The move operation involves no memory allocation or copying of data, but just a fixed number of pointer copies.

Community
  • 1
  • 1
Mankarse
  • 39,818
  • 11
  • 97
  • 141