|
| 1 | +# Return Value Optimization and Copy Elision |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +Suppose we wanted to write a function that returns a vector containing |
| 6 | +number from `0` to `n - 1`. The simplest version is |
| 7 | +**return-by-value**: |
| 8 | + |
| 9 | +```c++ |
| 10 | +std::vector<int> Range(int n) { |
| 11 | + std::vector<int> result; |
| 12 | + result.reserve(n); |
| 13 | + for (int i = 0; i < n; ++i) { |
| 14 | + result.push_back(i); |
| 15 | + } |
| 16 | +} |
| 17 | +``` |
| 18 | +
|
| 19 | +And we can call it like: |
| 20 | +
|
| 21 | +```c++ |
| 22 | +std::vector<int> x = Range(1000000); |
| 23 | +``` |
| 24 | + |
| 25 | +A valid question is: does this involves a copy or move of the huge |
| 26 | +``std::vector``? |
| 27 | + |
| 28 | +## The Answer |
| 29 | + |
| 30 | +If you are using a mordern compiler such |
| 31 | +as [gcc](https://gcc.gnu.org/) and [clang](http://clang.llvm.org/), |
| 32 | +the answer is **no** and **no**. A mechanism called **copy elision** |
| 33 | +will be enforced here as a **return value optimization**. |
| 34 | + |
| 35 | +The standard has some detailed description on when **copy elision** |
| 36 | +happens, but in general copy elision happens when one (or both) of the |
| 37 | +conditions are met (There are other cases but are not important enough |
| 38 | +to be highlighted here): |
| 39 | + |
| 40 | +1. Assigning a temporary to a variable of the same type. |
| 41 | +2. Returning a value right before it goes out of its scope. |
| 42 | + |
| 43 | +When copy elision happens, the variable gets the value of the |
| 44 | +temporary without calling the copy constructor nor the move |
| 45 | +constructor. In fact it claims the temporary and becomes it. |
| 46 | + |
| 47 | +## How about the Passing-A-Pointer Pattern |
| 48 | + |
| 49 | +Another pattern that is widely used for this is pass-a-pointer: |
| 50 | + |
| 51 | +```c++ |
| 52 | +void Range(int n, std::vector<int> *result) { |
| 53 | + result->reserve(n); |
| 54 | + for (int i = 0; i < n; ++i) { |
| 55 | + result->push_back(i); |
| 56 | + } |
| 57 | +} |
| 58 | +``` |
| 59 | +
|
| 60 | +This is acceptable, but I would say it is not as good bcause: |
| 61 | +
|
| 62 | +1. Do we assume the pointer `result` is initialized? A bad assumption |
| 63 | + can core dump here. |
| 64 | +2. It is far less readable, and it requires certain amount of mental |
| 65 | + work to realize that we are actually **returning** a vector. |
| 66 | + |
| 67 | +## Conclusion |
| 68 | +
|
| 69 | +Stick to the copy elision approach and let the compiler lift the |
| 70 | +weight. Do not try to outsmart the compiler by making your code less |
| 71 | +readable. |
| 72 | +
|
| 73 | +
|
0 commit comments