
Rvalue references
We cannot bind lvalue references to temporaries. The following code won't compile:
int get_it() {
int it{42};
return it;
}
...
int& impossible{get_it()}; // compile error
We need to declare an rvalue reference to be able to bind to temporaries (including literal values):
int&& possible{get_it()};
Rvalue references allow us to skip the generation of temporaries as much as possible. For example, a function that takes the result as an rvalue reference runs faster by eliminating temporary objects:
void do_something(int&& val) {
// do something with the val
}
// the return value of the get_it is moved to do_something rather than copied
do_something(get_it());
To imagine the effect of moving, imagine that the preceding code will be translated into the following (just to get the full idea of moving):
int val;
void get_it() {
val = 42;
}
void do_something() {
// do something with the val
}
do_something();
Before moving was introduced, the preceding code would look like this (with some compiler optimization):
int tmp;
void get_it() {
tmp = 42;
}
void do_something(int val) {
// do something with the val
}
do_something(tmp);
The move constructor, along with the move operator, =(), has the effect of copying without actually carrying out a copy operation when the input argument represents an rvalue. That's why we should also implement these new functions in the class: so that we can optimize the code wherever it makes sense. The move constructor can grab the source object instead of copying it, as shown here:
class Warehouse {
public:
// constructors omitted for brevity
Warehouse(Warehouse&& src)
: size_{src.size_},
capacity_{src.capacity_},
products_{src.products_}
{
src.size_ = 0;
src.capacity_ = 0;
src.products_ = nullptr;
}
};
Instead of creating a new array of capacity_ size and then copying each element of the products_ array, we just grabbed the pointer to the array. We know that the src object is an rvalue and that it will soon be destroyed, which means the destructor will be called and the destructor will delete the allocated array. Now, we point to the allocated array from the newly created Warehouse object, which is why we cannot let the destructor delete the source array. Due to this, we assign nullptr to it to make sure the destructor will miss the allocated object. So, the following code will be optimized because of the move constructor:
Warehouse large = small + mid;
The result of + operator will be moved rather than copied. Take a look at the following diagram:
The preceding diagram demonstrates how the temporary is being moved to the large object.