Rule of Three

Share the Article

Rule of three corresponds to automatic generation of 3 special member functions – constructor, destructor and assignment by C++ compiler.

In C++11, we know that if a class designer declares any one of the special member functions, then it means that there is something special about the way in which these special operations need to be done. Therefore, the class designer has to define all three and compiler will not generate other functions.

The compiler will not generate move operation, when

  • The user provides a move constructor
  • Or if user provides any of constructor or copy constructor
  • If user provides a destructor
  • Lastly user provides the assignment operator

In following example, move constructor of Base class will be called from Derived class

#include <iostream> //main header using namespace std; //namespace class BaseC { public: BaseC() { cout << "Base Constructor" << endl; } BaseC(const Base& rhs) { cout << "Base Copy Constructor" << endl; } BaseC(BaseC&& rhs) { cout << "Base Move Constructor" << endl; } }; class DerivedC : public BaseC { public: DerivedC() { cout << "Derived Constructor" << endl; } DerivedC(DerivedC&& rhs) : BaseC(std::move(rhs)) { cout << "Derived Move Constructor" << endl; } }; int main() { DerivedC d1; DerivedC d2 = std::move(d1); return 0; }

The output is:

Base Constructor Derived Constructor Base Move Constructor Derived Move Constructor

Now, in same example, consider that the class decides to delete user-defined move constructor in Base class. Now the compiler will not going to generate move constructor and eventually will call a copy constructor.

#include <iostream> //main header using namespace std; //namespace class BaseC { public: BaseC() { cout << "Base Constructor" << endl; } BaseC(const BaseC& rhs) //This will be called now { cout << "Base Copy Constructor" << endl; } // BaseC(BaseC&& rhs) //Move Constructor is Commented // { // cout << "Base Move Constructor" // << endl; // } }; class DerivedC : public BaseC { public: DerivedC() { cout << "Derived Constructor" << endl; } DerivedC(DerivedC&& rhs) : BaseC(std::move(rhs)) { cout << "Derived Move Constructor" << endl; } }; int main() { DerivedC d1; DerivedC d2 = std::move(d1); return 0; }

The output is:

Base Constructor Derived Constructor Base Copy Constructor Derived Move Constructor

Compatibility with old C++.

Another consequence of the Rule of Three is explained. When a class declares a destructor, then in ideal case, the compiler should not generate both the copy as well as the move operations. This is because they wouldn’t do the semantically correct operation.

However, it is too late to apply this rule now in old C++ and applying this rule now in old C++ would break the compatibility with Legacy code. Earlier, old C++ (C++98) did not applied this rule of three properly and therefore, they still generate functions in contradictions with rule of three.

C++11 has deprecated the automatic generation of copy & assignment for classes declaring copy operations or a destructor

Main Funda: If user intends to provide any of the special member function, then better he should provide all

Related Topics:

 What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *