C++ “rule of five” is the rules of thumb in modern C++ for the building of exception-safe code and for formalizing rules on resource management.
In this blog post, I would like to discuss the “rule of three” for classic C++, “rule of five” for modern C++, and their logics behind.
Rule of Three
The classic C++ has a “rule of three”. If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.
The logic behind the “rule of three” is that if the user ever wants to implement a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, rather than uses the implicit destructor, copy constructor, and copy assignment operator, the object of this class usually has raw pointers pointing to the data on the heap. To handle the data on the heap, user-defined destructor, user-defined copy constructor, and user-defined copy assignment operator are all required.
However, the “rule of three” is not usually enforced by the compiler, and the compiler will not even throw warning if the “rule of three” was not followed in the code. For example, if we intentionally comment out the user-defined destructor and the user-defined copy constructor from the rule_of_three class defined in the “The Rule of Three/Five/Zero”.
Of course, this program is problematic and will encounter problems during runtime.
Rule of Five
Move semantics have been introduced to C++ since C++11. As a result of that the “rule of three” has been extended to “rule of five”. If a class requires a user-defined destructor, a user-defined copy constructor, a user-defined copy assignment operator, a user-defined move constructor, a user-defined move assignment operator, it almost certainly requires all five.
The logic behind the “rule of five” is exactly the same as “rule of three”. It is all about the resource management.
The “rule of five” has been enforced by the compiler to some extent. Since C++11, the implicitly-declared copy constructor and copy assignment will be deleted if the class has a user-defined move constructor or move assignment operator. For example, a CustomString class that follows the “rule of five” has been implemented.
This is due to C++11 has to be compatible with the classic C++ which does not have move semantics. Because the rvalues std::move(string1) can be converted to const lvalue, copy constructor and copy assignment were called when the rvalue is encountered if there is no move constructor and move assignment overloading.