Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STL containers move semantics and return by value: how many times of copying get avoided away?

I know that in c++11 the move semantics have been implemented in the STL containers to avoid temporary objects. And people say that now it's perfect to write functions which return by value. But I have some confusions about on earth how many times of copying are actually get avoided. Please see the following example:

vector<int> myVector() {
    vector<int> res;
    res.push_back(4);
    res.push_back(5);
    return res;
}

vector<int> v = myVector(); 

My understanding is that in c++03, myVector returns a copy of res( 4, 5 copied once), when evaluating vector<int> v = myVector(); vector<int>'s copy constructor vector<int>(const vector<int> &) is invoked ( 4, 5 copied twice). However in c++11 with move semantics, I want to know which copy of 4 and 5 gets avoided? both? Is return value optimization also invoked to reduce one time of copying 4 and 5?

like image 689
Allanqunzi Avatar asked Dec 16 '14 03:12

Allanqunzi


1 Answers

There are two copies in C++03 and two moves in C++11.

In both C++03 and C++11 the copy/moves are subject to elision, and as such (in an example like this) no copy/move is likely to happen.

vector<int> myVector() {
  vector<int> res;
  res.push_back(4);
  res.push_back(5);
  return res;// <- Here we construct the return value with res
} 

// here we construct v with the return value of myVector:
vector<int> v = myVector(); 

The elision out of the res into the return value of myVector is somewhat fragile. In C++11, the move that could occur if elision fails is going to be nearly as free (as doing nothing) -- 3 pointer copies, and 3 pointer clears. In C++03, the copy that could occur if elision fails can be expensive (memory allocation and O(n) copies).

Elision is the name of the operation compilers can do in certain circumstances where two values are turned into one, even if that transformation would cause a change in behavior (ie, it fails the as-if test).

When this happens, the combined value has a lifetime of the union of the two lifetimes.

In the above case, the local variable res and the return value of myVector() can be elided by the NRVO rule (named return value optimization). The return value of myVector can be elided into v as it is a statement of the form A a = b where b is an expression that results in an anonymous value (the name of such anonymous values has changed from C++03 to C++11 so I won't use it), in particular the return value of myVector().

This cause the lifetime of res v and the return value of myVector() to be merged into one value lifetime. In practice, what happens is res is placed directly into where the return value of myVector() is supposed to go (according to the calling convention), and so is v, and destruction is of this combined value skipped until v goes out of scope, as is construction in the return statement and on v = myVector().

Or in other words, v is constructed directly in myVector.

like image 153
Yakk - Adam Nevraumont Avatar answered Sep 18 '22 12:09

Yakk - Adam Nevraumont