Copy elision is a common compile-time optimization in C++. In this blog post, I would like to show some examples of copy elision, discuss its mechanism at the compiler level and some of its caveats.
Copy Elision
Copy elision omits copy and move constructors, resulting in zero-copy pass-by-value semantics.
Usually, compilers, such as GCC, will enable copy elision optimization automatically. We could see from the following printouts that copy elision saves us a few move (or copy) operations from return value or pass-by-value.
1 2 3 4 5 6 7 8 9 10 11
$ g++ copy_elision.cpp -o copy_elision -std=c++14 $ ./copy_elision ===================================== Constructor being called. Memory address of `a`: 0x7ffff88f8bc0 Memory address of `b`: 0x7ffff88f8bc0 ------------------------------------- Constructor being called. Memory address of `a`: 0x7ffff88f8c10 Memory address of `c`: 0x7ffff88f8c10 =====================================
The GCC option -fno-elide-constructors allows us to disable copy elision so that we could see what’s happening without copy elision.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$ g++ copy_elision.cpp -o copy_elision -std=c++14 -fno-elide-constructors $ ./copy_elision ===================================== Constructor being called. Memory address of `a`: 0x7ffee0d019f0 Move constructor being called. Move constructor being called. Memory address of `b`: 0x7ffee0d01a40 ------------------------------------- Constructor being called. Memory address of `a`: 0x7ffee0d01a70 Move constructor being called. Move constructor being called. Memory address of `c`: 0x7ffee0d01af0 =====================================
Sometimes, the copy elision optimization could be disabled or compromised by some erroneous implementations without being aware by the user. In the following example ineffective_copy_elision.cpp, the only difference to copy_elision.cpp is that we return a rvalue reference std::move(a) instead of a value a in f() to enable a move instead of copy to the return value.
However, this explicit operation disabled copy elision optimization.
1 2 3 4 5 6 7 8 9 10 11 12 13
$ g++ ineffective_copy_elision.cpp -o ineffective_copy_elision -std=c++14 $ ./ineffective_copy_elision ===================================== Constructor being called. Memory address of `a`: 0x7ffe439d6a60 Move constructor being called. Memory address of `b`: 0x7ffe439d6ab0 ------------------------------------- Constructor being called. Memory address of `a`: 0x7ffe439d6ab0 Move constructor being called. Memory address of `c`: 0x7ffe439d6b00 =====================================
Copy Elision Optimization from Compiler
Jon Kalb has created a great presentation on copy elision to facilitate its understanding at the compiler level.
Return Value Pass-By-Value Optimization
Argument Passing Pass-By-Value Optimization
Caveats
Interestingly, if the copy constructor of a class does partial copy or the move constructor of a class does partial move, the behavior of the application might be different between copy elision enabled and disabled.
In addition, functions in which the variable being returned is not determined at compile time cannot benefit from copy elision. For example,