Marshall Cline coins the term “Rule of Three“, referring to the practice of explicitly defining the C++ Copy Constructor, Copy Assignment Operator, and Destructor together as a unit (where one is needed, likely the others are, too). The key reason for this rule was the implicit generation of copy even when they could cause harm.
n1377 – A Proposal to Add Move Semantics Support to the C++ Language – is submitted for consideration for C++0x, adding two new implicit functions: a Move Constructor and Move Assignment Operator.
Various Sources point out several significant problems with generating implicit move constructors and move assignment operators. The problems occur for reasons related to the original raison d’etre of the “Rule of Three”, and I considered the need for a new “Rule of Five”. If C++0x was to be a new language, a “Rule of Five” may have sufficed. But it isn’t. Current C++ code which is 100% correct could suddenly become broken due to implicit move generation. A programming “rule of thumb” was clearly not sufficient this time around, and something had to change.
The C++0x standards committee meeting had as its biggest issue the task of figuring out how to fix this problem, or otherwise completely remove implicit move. Thankfully, the issue was resolved by putting strict (but coherent) limitations on when implicit move is generated, but without eliminating implicit move altogether.
I consider the new rules an enforcement of a milder version of the “rule of five” by the compiler. Due to backwards compatibility the “rule of three” subset cannot be enforced, only deprecated.
So we will still have implicit move in C++0x, with coherent compiler enforced limitations on generation which actually add value to boot.
I have created a diagram to outline the new dependencies, where a directed edge means “won’t be implicitly defined if this is explicitly defined”. Green means “actually enforced”, blue means “merely deprecated”:
Notice that an implicit destructor is not dependent on anything. In practice, move/copy constructor/assignment are so tightly coupled that they can be grouped together.
I find this easier to visualize in this form:
These diagrams only describe a subset of conditions which must be met before implicit functions will be generated.
You can read all the rules in the current Draft Standard, but my rule-of-thumb is:
- Copy Constructor: members and base classes must be copyable, and cannot have any rvalue reference members.
- Move Constructor: members and base classes must be movable or trivially copyable.
- Copy Assignment: members and base classes must be copy assignable, and cannot have any const or reference members.
- Move Assignment: members and base classes must be move assignable, and cannot have any const or reference members.
Rvalue references and move semantics are extremely powerful and cool features, and not only because of potential efficiency gains.
Copy can be hard (or impossible) to implement, and move gives us more expressiveness in designs, and that is a wonderful thing.
Implicit move is important for both consistency (why should copy be implicit and special?) and for making use of this outstanding feature far less tedious.
I congratulate the C++ standards committee on this conclusion.