How does this work with return value optimization?

vector<int> f(const vector<int>& v) { vector<int> ret(v.size()); fill(ret.begin(), ret.end(), 123); copy(v.begin(), v.end(), ret.begin()); return ret; } int main() { vector<int> v(10); v = f(v); } 

If return optimization is applied for f, then the local variable ret has the same address as v in main. But if so, populating ret will delete the data in v before the copy. The code is correct without RVO, and optimization should not break behavior.

Is it safe or I do not understand RVO correctly?

+7
c ++
source share
3 answers

What's happening:

On the callerโ€™s side, a return slot is provided that can hold the result, which means that the caller provides memory for a variable of type std::vector<int> . He expects the called method to build the value and is responsible for calling the destructor when the result is no longer in use and frees memory (if necessary, it probably just lives on the stack).

The called function (which can live in another translation unit!) Will be without NRVO, so this is:

  • Provide a memory slot for ret .
  • Build the local variable ret in this memory slot.
  • Make stuff ...
  • Copy-build the return value in the provided memory slot by copying ret .
  • Call ret destructor.

Now, with NRVO, the decision to optimize this can be made in the translation block of the called function . It converts the above value to:

  • Build a ret in the memory of the method return slot.
  • Make stuff ...

There is no need to do anything, since the memory belongs and the destructor is called by the caller, and because the optimization is transparent to the caller :)

This, of course, cannot eliminate the assignment in v in your example. If you save the result in another variable, for example

 std::vector<int> w = f(v); 

NRVO will build ret directly into w memory (since it will be passed as a backward slot to f ).

+5
source share

The code is correct, your understanding of RVO is not exactly that:

 the local variable ret shares the same address as v 

It does not share the address of the variable you assigned. Technically, when you return an automatic local variable, it will be copied to a temporary one. RVO skips this part.

This happens as follows:

  create variable `ret` | copy ret to a temporary before returning | assign the temporary to v 

NRVO (in this case) will skip the second part.

Compilers are smart, though, don't be surprised if it just optimizes all of this, since there is no observable behavior at all.

+5
source share

RVO concerns the construction of new facilities. Obviously, basically, the compiler cannot pass v as the address of the return value, since the function will build a new vector<int> with the address and v already built. So will the address of the temporary. RVO is concerned with what happens in function: without RVO, the compiler would build ret and then copy it to the address passed as a hidden argument. On the call site, if the return value (copy) is used to build the object, then the compiler will skip the copy of the temporary object into the new object and simply pass the address of the object to be built. If the return value is equal but not used as an argument to the copy constructor, this is not so much what the compiler can do. You need an object of the correct type, so it must generate a temporary one.

0
source share

All Articles