In modern C++, rvalue references are a compound type like standard C++ references, which are referred to as lvalue references. New rvalue reference rules were set by the C++11 specifications. In many cases, data is copied that simply needs to be moved, that is, the original data holder need not retain the data. The rvalue reference can be used to distinguish cases that require copying versus cases that merely require moving data. In this post, we explain how we use rvalue references to eliminate unnecessary copying in C++.
How to eliminate unnecessary copying in modern C++?
In many cases, data is copied that simply needs to be moved, that is, the original data holder need not retain the data. An example is swapping data in two structures, so neither structure holds its previous data. It would be logically sufficient to simply switch the references to the data.
rvalue
references can be used to distinguish cases that require copying versus cases that merely require moving data. Since copying can be a lengthy operation, you want to avoid it, if possible.
If a function wants to copy something that has been passed to it as an rvalue, then it can do a move rather than a copy, because it knows that the value is temporary. If a function is passed an lvalue, it may need to do a full copy if copy elision doesn’t apply. You can distinguish these cases with the function signature.
Consider a class Tx
that has a clone function that makes a deep copy of class instances. You want to define a move function that moves an object’s value. This function can be overloaded as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Tx { public: Tx clone(Tx c) { return c; } Tx move(Tx& l) // Using lvalue reference { return l.clone(l); //returns a copy since we can't touch the original value } Tx move(Tx&& r) // using rvalue reference { return r; //returns a reference since we don't care about the temporary's value } }; |
As in the given class example, we can use our specific move function for both rvalues and lvalues as we show below:
1 2 3 4 5 6 |
Tx a, b, c; a = Tx(); b = a.move(a); //parameter is lvalue c = a.move(Tx()); //parameter is rvalue |
Note that the move
function for the rvalue parameter does very little, so it executes much more quickly than the move for an lvalue parameter.
We can use a similar technique for functions that need to make copies, such as copy constructors and assignment operators. Suppose we have a template class with a pointer to some other class, where clone is again a deep copy function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
template <class T> class PointerClass { private: T* pointer; public: // Regular constructor PointerClass(void); // Copy constructor for lvalues PointerClass(PointerClass& pcl) : pointer(pcl.pointer ? pcl.pointer.clone() : 0) {} //make full copy // Copy constructor for rvalues PointerClass(PointerClass&& pcr) : pointer(pcr.pointer) {pcr.pointer = 0;} }; |
Here, when the copy constructor takes an rvalue:
- Does a move, not a copy. That is, it simply returns a reference to the data.
- Treats the rvalue argument pcr just like an lvalue in its code.
- Leaves the rvalue object in a defined state so that it can safely be deleted.
Is there a full example of how to eliminate unnecessary copying in C++?
Here is the full example which helps explain how to eliminate unnecessary copying in C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> class Tx { public: Tx clone(Tx c) { return c; } Tx move(Tx& l) // Using lvalue reference { return l.clone(l); //returns a copy since we can't touch the original value } Tx move(Tx&& r) // using rvalue reference { return r; //returns a reference since we don't care about the temporary's value } }; int main() { Tx a, b, c, x; a = Tx(); b = a.move(a); //parameter is lvalue c = a.move(Tx()); //parameter is rvalue x = a.clone(a); } |
For more information on these kinds of features of rvalue, see Rvalue references for *this Proposal document.
C++ Builder is the easiest and fastest C and C++ IDE for building simple or professional applications on the Windows, MacOS, iOS & Android operating systems. It is also easy for beginners to learn with its wide range of samples, tutorials, help files, and LSP support for code. RAD Studio’s C++ Builder version comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for cross-platform UIs.
There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, there are Professional, Architect, or Enterprise version.
Design. Code. Compile. Deploy.
Start Free Trial
Free C++Builder Community Edition