How Library Authors Should Use rvalue References
Rvalue references are particularly important if you are writing a class whose objects might be put into a container such as a vector. Most readers have encountered the Rule of Three, which says that if your class has a destructor, it probably needs a copy constructor and copy-assignment operator, too. In C++0x, the Rule of Three sometimes expands to a Rule of Five:
class Foo {
// …
Foo(); // Default constructor
Foo(const Foo&); // Copy constructor
Foo& operator=(const Foo&);// Copy assignment
Foo(Foo&&); // Move constructor
Foo& operator=(Foo&&); // Move assignment
~Foo(); // Destructor
};
If your class has a destructor, you may want to write a move constructor and move assignment operator for it as well. In both cases, the move versions are permitted to change their argument's value, because the compiler will call the move constructor or move assignment operator only on rvalues. However, because even an rvalue will eventually be destroyed, the program must leave its parameter with a valid value. It doesn't matter what that value is; typically, it will be something trivial such as a null value.
If you define the move constructor and move assignment operator properly, the compiler will call it whenever it is appropriate to do so. If you don't define them, the compiler will quietly use the copy constructor and copy assignment operator.
This discussion has only scratched the surface of this vast topic. The main points to remember:
Rvaluereferences let class authors reuse the contents of objects that are about to go away instead of copying those contents and then destroying the originals.- This ability is particularly important for classes whose objects might be put in containers, such as
vector, that move their contents from time to time; it can make an enormous difference in execution time. Moveoperations also allow classes (such as the IO types) to say that they can be moved but not copied.- You indicate that a class can move its contents by defining a
moveconstructor andmoveassignment operator to go along with thecopyconstructor andcopyassignment operator. - The
moveconstructor and assignment are allowed to change the contents of their source, but they must leave their source as a valid value so that when the destructor is eventually called, it doesn't crash. - If you don't define a
moveconstructor ormoveassignment, but your class does define its owncopyconstructor and assignment, then the compiler will use thecopyconstructor and assignment. Doing so yields sensible results, but foregoes the optimization that themoveversions might offer. - You can call
std::moveexplicitly on an object. It returns that object as anrvaluereference, so that if you copy or assign the result ofstd::move, doing so will call themoveconstructor or assignment operator, if one exists.
Constructors Made Easier
C++0x lets you give a default initial value to a class data member:
class Foo {
public:
Foo() { }
Foo(int a): m(a) { }
Foo(int a, int b): m(a), n(b) { }
private:
int a = 0, b = 0; // New in C++0x
std::string myName("Foo"); // New in C++0x
// …
};
The ability to give initial values to data members such as a, b, and myName is new in C++0x. When we do so, we are saying what values we want those members to have if the constructor doesn't say otherwise. Therefore, in this example, calling Foo() initializes both a and b to 0, Foo(42) initializes a to 42 and b to 0, and Foo(42, 24) initializes a to 42 and b to 24. In all cases, myName is initialized to "Foo".
This feature is especially convenient for classes with many constructors. Adding a data member to such a class used to require adding an initializer to every constructor. Under C++0x, it is possible to add one initializer to the data member's declaration, and then change only those constructors that give that member an unusual initial value.
C++0x makes it easier to write constructors in four other ways as well: You can
- declare a constructor as
= deleteto say that no one is allowed to use it. - declare a constructor as
= defaultto tell the compiler to generate it even if it would not usually do so (e.g., a default constructor). - delegate initialization to another constructor in the class by writing a constructor initializer that passes values to that constructor
- write a using-declaration to copy a constructor from a base class.
If, for example, we wanted to make our Foo class movable but not copyable, we might add the following member declarations:
Foo(const Foo&) = delete; Foo& operator=(const Foo&) = delete; Foo(Foo&&); Foo& operator=(Foo&&);
and then define the move constructor and move assignment operator appropriately.


