Copy Elision and Return Value Optimization in C++
In C++ computer programming, copy elision refers to a compiler optimization technique that eliminates unnecessary copying of objects.
…
Return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function’s return value.from Wikipedia
Start with an example
Considering the following code, how many times will the string copy constructor be called?
1 |
|
You might think,
- In
GenString()
,foo
is returnedN
times, the constructor is calledN
times. - In
GenVector()
, the temporary returned value ofGenString()
is pushed to vector, that’s anotherN
copy times. - If
N
is a large number, vector resizes automatically, it will copy values to new vector, roughly speaking, that’sO(N)
times. Why? Ask in the comments if you have question. - In
main()
, the temporary returned vector is passed by value to the constructor of OneStruct,O(N)
copy times. - In struct, it simply swap the value,
O(1)
time, no copy.
Looks reasonable, but in C++ 11 actual answer is ZERO.
As long as there are no two distinct names for the same value, it will not invoke the copy constructor. In the above example, the string can be named as v[0], in[0] or even without name at all (the return value of GenString()), but it never has more than one name at a time. The value is passed by two features: copy elision and move semantics.
Copy Elision
Copy elision happens whenever an object is initialized by copying another object of the same type, and the source object is no longer accessible afterwards, e.g. leave current scope. In this case, compiler treats it as two objects are holding the same place, just skip the copy constructor and replace the place with the new name. There are two major cases where copy elision would happen: returning a local variable inside a function, and initializing a variable with a temporary value.
Return a local variables
1 | string dosth() { |
Some people prefer to create a function and return a pointer rather than a value, thinking that return a large object is expensive, because it invokes object copy. However, thanks to copy elision, the out_str
will take the ownership of the local variable right after the function exits, it’s completely free.
Avoid object copy by passing by value
1 | // It invokes string copy inside. |
It may come as a surprise if you are accustomed to always passing objects by const-reference for efficiency. In the second function, instead of taking a string by const reference, and then copying it in the function body, it takes the variable by value, and then cheaply move it to local variable. Why? In fact in the second function it moves the copy operation from the function body to outside caller, which happens to meet the condition of copy elision, so it saves the one-time copy operation.
Pay attention
Not all compilers support copy elision, check if it is enabled first.
The primary limitation of copy elision is that it applies only during initialization of the destination object (i.e. when it’s first created), and requires the source object to be completely inaccessible after the copy.
Generally speaking, you should prefer simpler, safer, more readable code, and only go for something more complex if you have concrete evidence that the complex version performs better and that the difference matters. That principle certainly applies to this technique: passing by const reference is simpler and safer, so it’s still a good default choice. However, if you’re working in an area that’s known to be performance-sensitive, or your benchmarks show that you’re spending too much time copying function parameters, passing by value can be a very useful tool to have in your toolbox.