C++ Core Guidelines: The Rule of Zero, Five, or Six


This post is about the rule of zero, five, or maybe six. I will also show the difference between copy and reference semantic and a quite similar topic: deep versus shallow copy.

To be precise, C++ has about 50 rules for managing the lifecycle of an object. This time I will write about the three very import default operation rules. I provide you the link to each of the rules of the C++ core guidelines. If necessary, you can read the details following the link. Let's start.

C++ provides six default operations, sometimes also called special functions, for managing the lifecycle of an object. Consequently, this first post to the lifecycle of objects has to start with the six operations.  RuleOfZeroFiveSix


  • a default constructor: X()
  • a copy constructor: X(const X&)
  • a copy assignment: operator=(const X&)
  • a move constructor: X(X&&)
  • a move assignment: operator=(X&&)
  • a destructor: ~X()

The default operations are related. This means if you implement or =delete one of them, you have to think about the five others. The word implement may seem a little bit confusing. For the default constructor it means that you can define it or request if from the compiler:

X(){};          // explicitly defined
X() = default;  // requested from the compiler


This rule holds also for the five other default operations.

One general remark before I write about the set of default operations rules. C++ provides value semantic and not reference semantic for its types. Here is the best definition I found of both terms from https://isocpp.org/wiki/faq/value-vs-ref-semantics

  • Value semantic: Value (or “copy”) semantics mean assignment copies the value, not just the pointer.
  • Reference semantic: With reference semantics, assignment is a pointer-copy (i.e., a reference).

Here are the first three rules.

Set of default operations rules:

C.20: If you can avoid defining any default operations, do

This rule is also known as "the rule of zero". That means, if your class needs no default operations because all its members have the six special functions, you are done.

struct Named_map {
    // ... no default operations declared ...
    string name;
    map<int, int> rep;

Named_map nm;        // default construct
Named_map nm2 {nm};  // copy construct


The default construction and the copy construction will work because they are already defined for std::string and std::map.

C.21: If you define or =delete any default operation, define or =delete them all

Because we have to define or =delete all six of them, this rule is called "the rule of five". Five seems strange to me. The reason for the rule of five or six is quite obvious. The six operations are closely related; therefore, the propability is very high that you will get very odd objects if you don't follow the rule. Here is an example from the guidelines.

struct M2 {   // bad: incomplete set of default operations
    // ...
    // ... no copy or move operations ...
    ~M2() { delete[] rep; }
    pair<int, int>* rep;  // zero-terminated set of pairs

void use()
    M2 x;
    M2 y;
    // ...
    x = y;   // the default assignment
    // ...


What is strange about this example? First, the destructor deletes rep, which was never initialised. Second, and that is more serious. The default copy assignment operation  (x  =  y) in the last line copies all members of M2. This means, in particular, that pointer rep will be copied. Hence, the destructor for x and y will be called and we get undefined behaviour because of double deletion.

C.22: Make default operations consistent

This rule is kind of related to the previous rule. If you implement the default operations with different semantic, the users of the class may become very confused. This is the reason, I constructed the class Strange.To observe the odd behaviour, Strange includes a pointer to int.

// strange.cpp (https://github.com/RainerGrimm/ModernesCppSource)

#include <iostream>
struct Strange{ Strange(): p(new int(2011)){} // deep copy Strange(const Strange& a) : p(new int(*(a.p))){} // (1) // shallow copy Strange& operator=(const Strange& a){ // (2) p = a.p; return *this; } int* p; }; int main(){ std::cout << std::endl; std::cout << "Deep copy" << std::endl; Strange s1; Strange s2(s1); // (3) std::cout << "s1.p: " << s1.p << "; *(s1.p): " << *(s1.p) << std::endl; std::cout << "s2.p: " << s2.p << "; *(s2.p): " << *(s2.p) << std::endl; std::cout << "*(s2.p) = 2017" << std::endl; *(s2.p) = 2017; // (4) std::cout << "s1.p: " << s1.p << "; *(s1.p): " << *(s1.p) << std::endl; std::cout << "s2.p: " << s2.p << "; *(s2.p): " << *(s2.p) << std::endl; std::cout << std::endl; std::cout << "Shallow copy" << std::endl; Strange s3; s3 = s1; // (5) std::cout << "s1.p: " << s1.p << "; *(s1.p): " << *(s1.p) << std::endl; std::cout << "s3.p: " << s3.p << "; *(s3.p): " << *(s3.p) << std::endl; std::cout << "*(s3.p) = 2017" << std::endl; *(s3.p) = 2017; // (6) std::cout << "s1.p: " << s1.p << "; *(s1.p): " << *(s1.p) << std::endl; std::cout << "s3.p: " << s3.p << "; *(s3.p): " << *(s3.p) << std::endl; std::cout << std::endl; std::cout << "delete s1.p" << std::endl; delete s1.p; // (7) std::cout << "s2.p: " << s2.p << "; *(s2.p): " << *(s2.p) << std::endl; std::cout << "s3.p: " << s3.p << "; *(s3.p): " << *(s3.p) << std::endl; std::cout << std::endl; }


The class Strange has a copy constructor (1) and a copy assignment operator (2). The copy constructor uses deep copy and the assignment operator shallow copy. Most of the times you want deep copy semantic (value semantic) for your types but you probably never want to have different semantic for this two related operations.

The difference is, that deep copy semantic creates two separated new objects (p(new int(*(a.p)) while shallow copy semantic just copies the pointer (p = a.p). Let's play with the Strange types. Here is the output of the program.


In the expression (3) I use the copy constructor to create s2.  Displaying the addresses of the pointer and changing the value of the pointer s2.p (4) shows, s1 and s2 are two distinct objects. That will not hold for s1 and s3. The copy assignment in expression (5) triggers a shallow copy. The result is that changing the pointer s3.p (6)  will also affect the pointer s1.p; therefore both pointers have the same value.

The fun starts if I delete the pointer s1.p (7). Because of the deep copy, nothing bad happened to s2.p; but the value becomes s3.p a null pointer. The be more precise: dereferencing a null pointer such as in (*s3.p) is undefined behaviour.

What's next

The story of the C++ core guidelines to the lifecycle of objects goes on. It continues with the rules for destruction of objects. This is also my plan for the next post.



Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner,  Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Avi Kohn, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, and Tobi Heideman.


Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, and Richard Sargeant.

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small



I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

Bookable (Online)



Standard Seminars 

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.


Contact Me

Modernes C++,


Tags: classes


+2 #1 Remco Melis 2019-02-22 00:44
Thanks very nice blog!
0 #2 MIROSLAV HOLEC 2020-12-02 19:28
Dear Rainer, this is a huge work done here on these sites!! Thanks thanks thanks, this will be mighty, to go it through!

My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 4773

Yesterday 8489

Week 21788

Month 21788

All 5809464

Currently are 173 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments