The rules for copy and move are pretty obvious. But before I describe them I have to write about the two remaining rules for constructors. They are about delegating and inheriting constructors.
Here are the two remaining rules:
Since C++11, a constructor can delegate its work to another constructor of the same class. This is the modern way in C++ to put common actions for all constructors in one constructor. In C++ code before C++11, you often used for such a job an init function.
The constructors (2) and (3) of the class Degree delegate all its initialization work to the constructor (1), which verifies its arguments. Invoking constructors recursively is undefined behavior.
C.52: Use inheriting constructors to import constructors into a derived class that does not need further explicit initialization
If you can reuse constructors of the base class in the derived class, do it. You violate the DRY (Don’t Repeat Yourself) principle if you don’t do it.
There is a danger in using inheriting constructors. If your derived class, such as Rec2 has its own members, they are uninitialized (1).
Copy and Move
The chapter starts with a meta-rule. Values types, which behave like an int, should be copyable, but interfaces in class hierarchies are not. The last rule C.67 refers to this meta-rule.
Here are the eight rules:
- C.60: Make copy assignment non-
virtual, take the parameter by
const&, and return by non-
- C.61: A copy operation should copy
- C.62: Make copy assignment safe for self-assignment
- C.63: Make move assignment non-
virtual, take the parameter by
&&, and return by non-
- C.64: A move operation should move and leave its source in a valid state
- C.65: Make move assignment safe for self-assignment
- C.66: Make move operations
- C.67: A base class should suppress copying, and provide a virtual
cloneinstead, if “copying” is desired
The first six rules for copy and move consist of 3 similar pairs; therefore, I can explain them together.
- C.60 and C.63 state that you should make the copy (move) assignment non-virtual and return a non-const reference. There is a difference in the way you should take the parameter.
- Copy assignment should take its parameter by a const lvalue reference (&) because you should not change the source of your assignment
- Move assignment should take its parameter by a non-const rvalue reference (&&) because you have to modify the source of your assignment
- This is the pattern the assignment operators of the standard template library follow. Here is a simplified look at std::vector.
- C.61 and C.64 say a copy (move) operation should copy (move). This is the expected semantic for a = b.
- In case of copying, this means that after copying a and b (a = b) a must be the same: (a ==b).
- Copying can be a deep or shallow. Deep copying means that both objects a and b are afterwards totally independent of each other (value semantic). Shallow copying means that both objects a and b share an object afterwards (reference semantic).
- C.64 states the moved-from object should be in a valid state. Most of the times this is the default state of the source. The C++ standard requires, that the moved-from object must be afterwards in an unspecified but valid state.
- C.62 and C.65 state the same. Copy (move) assignment should be safe for self-assignment. x = x should not change the value of x.
- Copy (move) assignment of the containers of the STL, std::string, and built-in type such as int is safe for self-assignment; therefore, the default generated copy (move) assignment operator is, in this case, safe for self-assignment. The same will hold for an automatically generated copy (move) assignment operator, which uses types that are safe for self-assignment.
The code snippet shows that there is no test for self-assignment such as in the next example necessary. Here is the version of the type Foo with redundant (expensive) checks (1) and (2) for self-assignment.
This is the pattern the move operators of the standard template library follow. Have a look at std::vector.
The last rule C.67 deserves more attention.
C.67: A base class should suppress copying, and provide a virtual
clone instead, if “copying” is desired
The main reason for this rule is that slicing is not possible. Slicing is one of these phenomena in C++, and my colleagues always warned me. There also exists an article on Wikipedia about object slicing.
Slicing will happen when an object of a derived class is copied to an object of a base class.
In this scenario, the copy operations of the base class are used; therefore, only the base part of d is copied.
From the object-oriented perspective, an instance of Derived is-a an instance of Base. That means whenever you need an instance of Base you can use an instance of Derived. But you have to be careful. If you take the instance of Base by copy (value-semantic), you will only get the base parts of an instance of Derived.
The cure that the guidelines suggest is that the base class should suppress copying but provide a virtual clone member function if copying is desired. Here is an example from the guidelines.
The clone method returns the newly created object in a std::unique_ptr; therefore, the ownership goes to the caller. Such a clone method is better known as a factory method. A factory method is one of the creational patterns from the book: Design Pattern: Elements of Reusable Object-Oriented Software.
There are a few rules for default operations left. The next post deals with comparisons, swap, and hash.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,