
David A. answered 10/11/19
Experienced C++ tutor who loves to mentor and pass on my knowledge
The && means "rvalue reference", often pronounced "ref ref" :) Learning about rvalue references is crucial in your learning of modern C++ (aka C++11 and later), for one, move semantics would not be possible.
You are now probably saying "What the heck is an rvalue reference anyway! :) To answer this question, we need to start with what is an lvalue and rvalue.
lvalue and rvalue
Generally speaking, lvalue means left value and rvalue means right value. Loosely speaking, an lvalue is said to be something that can sit on the left-hand side of an assignment and an rvalue can sit on the right-hand side of an assignment. That's not the entire picture, but it a good starting point (at least from the early days of C before C++ :)
In general, the following is true:
- Whatever has a name is an lvalue
- int anInt = 1;
- int &ref = anInt;
- An lvalue is a mutable object (it has a memory location; i.e. an address)
- You can take the address of an lvalue (e.g. &variable) but not with an rvalue
- An rvalue is a temporary object that can only be placed on the right-hand-side of an assignment
- A lvalue can be placed on both the left-hand-side and right-hand-side of an assignment
OK, this is a bunch of academia terminology, right? I don't really care do I? Or do I?
Answer: You do care. Read on...
In order to understand rvalue references (&&), you first need to know that rvalues refer to unnamed (temporary) objects that have no address (can't take the address for these things). Now let's review references...
Reference review:
#include <iostream>
int main(int argc, const char * argv[]) {
int x = 10; // x is an lvalue
int &ref = x; // ref is a lvalue reference (I can take the address of ref)
std::cout << "The address of ref is really the same as x: " << &x
<< ':' << &ref << '\n';
// int &&rref = x; // rvalue reference to type 'int' cannot bind to
// lvalue of type 'int'
int &&rref = 100;
std::cout << "Value of rref: " << rref << '\n';
// I can't take the address of an rvalue, but I *can* take the address
// of an rvalue reference variable because it has a name :)
//
std::cout << "Address of rref: " << &rref << '\n'; // Valid
//
//std::cout << "Address of rvalue: " << &100 << '\n'; // Error: Cannot take the address of an rvalue
return 0;
}
The rvalue reference used in the example above is not really useful, as you wouldn't typically define an rvalue reference in this matter. So how should I really use them?
Real use for rvalue references (T&&)
The real use for rvalue references is to use them for function parameters in order to support "Move Semantics". Let's explain...
To start, let's say we have two functions:
printReference (const std::string& str) {
std::cout << "rvalue reference: " << str << '\n';
}
printReference (std::string&& str) {
std::cout << "rvalue reference: " << str << '\n';
}
The first function that takes a const lvalue reference will accept any argument that it is given, whether it be an lvalue or an rvalue, and regardless of whether the lvalue or rvalue is mutable or not. However, in the presence of the second (overloaded) function taking an rvalue reference, it will be given all values except mutable rvalue-references.
Let's call these functions:
With rvalue references, we have a way to determine if a reference variable refers to a temporary object (rvalue) or to a permanent object (lvalue)
Move Semantics
The example above using printReference has an interesting side-effect, but it still doesn't have all that
much value. The value for distinguishing lvalues and rvalues really come into play when dealing with larger objects with memory ownership. Copying large objects is very expensive, especially if we are passing temporary (rvalue) objects by value. Wouldn't it be nice if we could steal the memory used by a temporary object,rather than creating a temporary object to copy to a newly created object (2 copies of a very large object, where the temporary object ends up getting destroyed at the end of the function). This is only possible if we can distinguish between an lvalue and rvalue.
To learn more about Move Semantics, read my answer to "What is C++11 Move Semantics?"