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 value optimization is applied to f, then the local variable ret shares the same address as v in main. But if this is true, filling ret will trash the data in v before the copy. The code is correct without RVO and optimizations shouldn't break behavior.
Is this safe or do I not understand RVO correctly?
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
> Note also that C doesn't have return-value-optimization, hence all your struct-returning functions will cause a call to memcpy (won't happen when compiled in C++ mode of course).
One form of copy elision is known as "named return value optimization" - a.k.a NRVO. Another one is known as "return value optimization" - a.k.a RVO. There are also other forms of copy elision which don't involve returned values.
If “-fno-elide-constructors” option is used, first default constructor is called to create a temporary object, then copy constructor is called to copy the temporary object to ob.
What happens is this:
On the caller's side, a return slot is provided which can hold the result, that means that the caller provides the memory for the variable of type std::vector<int>
. It expects the called method to construct the value and is itself responsible for calling the destructor when the result is no longer used and freeing the memory (if necessary, it probably just lives on the stack).
The called function (which may live in a different translation unit!) would, without the NRVO, so this:
ret
.ret
in this memory slot.ret
.ret
's destructor.Now, with the NRVO, the decision to optimize this can be done in the called function's translation unit. It transforms the above into:
ret
in the memory of the method's return slot.No need to do anything else as the memory is owned and the destructor is called by the caller and because the optimization is transparent for the caller :)
This, of course, can't eliminate the assignment into v
in your example. If you store the result in a different variable, e.g.
std::vector<int> w = f(v);
the NRVO will construct ret
directly into w
's memory (as this will be passed in as the return slot to f
).
The code is correct, your understanding of RVO is not - specifically this:
the local variable ret shares the same address as v
It doesn't share the address of the variable you assign it to. Technically, when you return an automatic local variable, it will be copied to a temporary. RVO skips that part.
It goes like this:
create variable `ret`
|
copy ret to a temporary before returning
|
assign the temporary to v
NRVO (in this case) would skip the second part.
Compilers are smart though, so don't be surprised if it just optimizes the whole thing, since there's no observable behavior at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With