Skip to content

Sutter Assignment Operator Copy Constructor Inheritance

C++11 Language Extensions – Classes

and

The common idiom of “prohibiting copying” can now be expressed directly:

Conversely, we can also say explicitly that we want to default copy behavior:

Explicitly writing out the default by hand is at best redundant, and has two drawbacks: it sometimes generates less efficient code than the compiler-generated default would, and it prevents types from being considered PODs. However, comments about copy operations and (worse) a user explicitly defining copy operations meant to give the default behavior were not uncommon in pre-C++11 code. Leaving it to the compiler to implement the default behavior is simpler, less error-prone, and often leads to better object code.

The mechanism can be used for any function that has a default. The mechanism can be used for any function. For example, we can eliminate an undesired conversion like this:

See also:

For further historical background of alternatives, see

Control of default move and copy

By default, a class has five operations:

  • copy assignment
  • copy constructor
  • move assignment
  • move constructor
  • destructor

If you declare any of those you must consider all and explicitly define or the ones you want. Think of copying, moving, and destruction as closely related operations, rather than individual operations that you can freely mix and match – you can specify arbitrary combinations, but only a few combinations make sense semantically.

If any move, copy, or destructor is explicitly specified (declared, defined, , or ) by the user, no move is generated by default. If any copy or destructor is explicitly specified (declared, defined, , or ) by the user, any undeclared copy operations are generated by default, but this is deprecated, so don’t rely on that. For example:

This implicitly also disallows moving of s. Copy initialization is allowed, but deprecated.

This implicitly also disallows moving of s. Copy initialization is allowed, but deprecated.

This implicitly also disallows copying of s.

This implicitly also disallows moving of s. Copying is allowed, but deprecated.

If you declare one of these five function, you should explicitly declare all. For example:

See also:

Delegating constructors

In C++98, if you want two constructors to do the same thing, repeat yourself or call “an function.” For example:

Verbosity hinders readability and repetition is error-prone. Both get in the way of maintainability. So, in C++11, we can define one constructor in terms of another:

See also:

  • the C++ draft section 12.6.2
  • N1986==06-0056 Herb Sutter and Francis Glassborow: Delegating Constructors (revision 3).
  • ECMA-372 for a description of this feature as originally designed in C++/CLI before being proposed for ISO C++.

In-class member initializers

In C++98, only members of integral types could be initialized in-class, and the initializer has to be a constant expression. These restrictions ensured that the compiler can do the initialization at compile-time. For example:

The basic idea for C++11 was to allow a non-static data member to be initialized where it is declared (in its class). A constructor can then use the initializer when run-time initialization is needed. Consider:

This is equivalent to:

This saves a bit of typing, but the real benefits come in classes with multiple constructors. Often, all constructors use a common initializer for a member:

The fact that and each have a single default is lost in the mess of code and could easily become a problem during maintenance. Instead, we can factor out the initialization of the data members:

If a member is initialized by both an in-class initializer and a constructor, only the constructor’s initialization is done (it “overrides” the default). So we can simplify further:

See also:

Inherited constructors

People sometimes are confused about the fact that ordinary scope rules apply to class members. In particular, a member of a base class is not in the same scope as a member of a derived class:

In C++98, we can “lift” a set of overloaded functions from a base class into a derived class:

Stroustrup has said that “Little more than a historical accident prevents using this to work for a constructor as well as for an ordinary member function.” C++11 provides that facility:

If you so choose, you can still shoot yourself in the foot by inheriting constructors in a derived class in which you define new member variables needing initialization:

You might remove the bullet from your foot by using a member-initializer:

See also:

Override controls:

No special keyword or annotation is needed for a function in a derived class to override a function in a base class. For example:

This can cause confusion (what did the programmer mean?), and problems if a compiler doesn’t warn against suspicious code. For example,

  • Did the programmer mean to override ? (almost certainly yes).
  • Did the programming mean to override ? (probably not because of the redundant explicit ).
  • Did the programmer mean to override ? (probably, but that’s not possible).

To allow the programmer to be more explicit about overriding, we now have the “contextual keyword” :

A declaration marked is only valid if there is a function to override. The problem with is not guaranteed to be caught (because it is not an error according to the language definition) but it is easily diagnosed.

is only a contextual keyword, so you can still use it as an identifier:

See also:

Override controls:

Sometimes, a programmer wants to prevent a virtual function from being overridden. This can be achieved by adding the specifier . For example:

There are legitimate reasons for wanting to prevent overriding, but it should be noted that many examples used to motivate have been based on mistaken assumptions on how expensive virtual functions are (usually based on experience with other languages). So, if you feel the urge to add a specifier, please double check that the reason is logical: Would semantic errors be likely if someone defined a class that overrode that virtual function? Adding closes the possibility that a future user of the class might provide a better implementation of the function for some class you haven’t thought of. If you don’t want to keep that option open, why did you define the function to be in the first place? Most reasonable answers to that question encountered to date have been along the lines: This is a fundamental function in a framework that the framework builders needed to override but isn’t safe for general users to override. Be suspicious towards such claims and be sure is really appropriate.

If it is performance (inlining) you want or you simply never want to override, it is typically better not to define a function to be in the first place. This is not Java.

is only a contextual keyword, so you can still use it as an identifier:

See also:

  • Standard: 10 Derived classes [class.derived] [9]
  • Standard: 10.3 Virtual functions [class.virtual]

Explicit conversion operators

C++98 provides implicit and constructors; that is, the conversion defined by a constructor declared can be used only for explicit conversions whereas other constructors can be used for implicit conversions also. For example:

However, a constructor is not the only mechanism for defining a conversion. If we can’t modify a class, we can define a conversion operator from a different class. For example:

Unfortunately, C++98 had no conversion operators, largely because there are far fewer problematic examples. C++11 deals with that oversight by allowing conversion operators to be . For example:

See also:

We’ve had years to get used to the old rules for making classes copyable. Juan Alday sees how the new standards change them.

The Rule of Three, considered good practice for many years, became the Rule of Five under C++11. A proper application of the Rule of Five and resource management makes users transition to the Rule of Zero, where the preferable option is to write classes that declare/define neither a destructor nor a copy/move constructor or copy/move assignment operator.

Introduction

The rule of three [Koenig/Moo1] is a rule of thumb coined by Marshall Cline, dating back to 1991. It states that if a class defines a destructor it should almost always define a copy constructor and an assignment operator.

In reality it is two rules:

  1. If you define a destructor, you probably need to define a copy constructor and an assignment operator
  2. If you defined a copy constructor or assignment operator, you probably will need both, as well as the destructor

Although considered good practice, the compiler can’t enforce it. The C++ standard [N1316] mandates that implicit versions will be created if a user doesn’t declare them explicitly:

§ 12.4 / 3 If a class has no user-declared destructor, a destructor is declared implicitly

§ 12.8 / 4 If the class definition does not explicitly declare a copy constructor, one is declared implicitly.

§ 12.8 / 10 If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly

This can lead to code that is fundamentally broken, yet syntactically valid:

struct A { A(const char* str) : myStr(strdup(str)) {} ~A() {free(myStr);} private: char* myStr; }; int main() { A foo(“whatever”); A myCopy(foo); }

(Note: Most static analysis tools will detect these errors, but that’s beyond the scope of this article)

C++11 [N3242] added wording to the Standard, deprecating the previous behavior.

D.3 Implicit declaration of copy functions [depr.impldec]

The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor. In a future revision of this International Standard, these implicit definitions could become deleted

This means that compilers keep generating a defaulted copy constructor, assignment operator and destructor if no user-defined declaration is found, but at least now they might issue a warning.

Before and after the adoption of C++11 there were ongoing discussions on the need to ban, rather than deprecate this behavior. C++14 [N3797] kept the same compromise but that does not mean that C++17 will not switch to a full ban [N3839] if adequate wording can be drafted, thus enforcing the need to properly enforce the Rule of Three as a standard of best practices.

C++11 introduced move operations, transforming the Rule of Three into the Rule of Five, implicitly generating move operations under specific circumstances.

There was a lot of controversy regarding automatic generation of an implicit move constructor and assignment operator [Abrahams1] [Abrahams2] [N3153] [N3174] [N3201] [N3203], and the wording was adjusted to reach a compromise and tighten the rules under which they would get defaulted.

C++11 [N3242] says that move operations are ONLY implicitly declared if the following set of conditions occur:

§ 12.8 / 9

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared move assignment operator,
  • X does not have a user-declared destructor, and
  • The move constructor would not be implicitly defined as deleted.

§ 12.8 / 20

If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared move constructor,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared destructor, and
  • The move assignment operator would not be implicitly defined as deleted.

Unlike the Rule of three, the Rule of Five is partially enforced by the compiler. You get a default move constructor and move assignment operator if and only if none of the other four are defined/defaulted by the user.

C++14 expanded the wording, and now an explicit declaration of a move constructor or move assignment operator marks the defaulted copy constructor and assignment operator as deleted. This means that explicit move operations make your objects non-copyable/assignable by default. This is as close as you get to a real enforcement of the rule of five by a compiler.

§ 12.8 / 7

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

§ 12.8 / 18

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.

At this point, the Rule of Five transitions in fact to the Rule of Zero, a term coined by Peter Sommerlad [Sommerlad1]:

Write your classes in a way that you do not need to declare/define neither a destructor, nor a copy/move constructor or copy/move assignment operator

Use smart pointers & standard library classes for managing resources

There are two cases where users generally bypass the compiler and write their own declarations:

  1. Managed resources
  2. polymorphic deletion and/or virtual functions

Managed resources

When possible, use a combination of standard class templates like and with custom deleters to avoid managing resources [Sommerlad1]

In C++98/03, a class managing resources would look something like Listing 1. In C++11/14, with move operations, it could be implemented as in Listing 2.

Applying the Rule of Zero, the code would be more expressive (Listing 3).

By using a we make our class non-copyable/assignable and identical in behavior to the previous examples.

and help us manage pointer types. For non-pointers, Sommerlad and Sandoval [N3949] have proposed two additional RAII wrappers: and , to tie zero or one resource to a cleanup function that gets selectively triggered on scope exit. If it gets accepted, users will have a standard way of managing almost any type of resource automatically.

Users should try to follow this pattern as much as possible and only customize their code when there is no clear alternative. For those cases where we are forced to manage resources (vendor APIs, etc), Martinho Fernandes [Fernandes1] further extends the definition, tying it to the Single Responsibility Principle:

Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership

Polymorphic deletion / virtual functions

One question on the rule of zero is what to do when we want to support polymorphic deletion, or when our classes have virtual functions:

For years it has been taught that classes supporting inheritance and/or with virtual functions should usually have a virtual destructor. [Stroustrup1] [Koenig/Moo2]

Note: In reality, not all base classes with virtual functions need virtual destructors. Herb Sutter’s advice [Sutter1]:

If A is intended to be used as a base class, and if callers should be able to destroy polymorphically, then make A::~A public and virtual. Otherwise make it protected (and not-virtual)

So a base class with a virtual function like

struct A { virtual void foo(); };

should be written, following standard practices, as:

struct A { virtual ~A() {} virtual void foo(); };

One side effect is that now both classes are different. After declaring the destructor, doesn’t support move operations. You still get copy and assignment, but that’s as far as you can go.

In C++11 the correct way to define it, in order to get move semantics is:

struct A { virtual ~A() =default; A(A&&)=default; A& operator=(A&&)=default; virtual void foo(); };

And in C++14 we need to define all five, since otherwise we disable copy/assignment:

struct A { virtual ~A() = default; A(const A&)=default; A& operator=(const A&)=default; A(A&&)=default; A& operator=(A&&)=default; virtual void foo(); };

(Note: Depending on your class needs you might also want to add a defaulted constructor, as the implicitly generated default constructor would be marked as deleted since we have specified a copy/move constructor.)

In this case we have applied a consistent rule of five, defaulting all five functions due to the virtual destructor. The question is: Do we really need to do that? Why can’t we apply the Rule of Zero?

The second part of the Rule of Zero (“Use smart pointers & standard library classes for managing resources”) [Sommerlad1] gives us the answer:

Under current practice, the reason for the virtual destructor is to free resources via a pointer to base. Under the Rule of Zero we shouldn’t really be managing our own resources, including instances of our classes (see Listing 4).

We have removed all the default declarations from our base class, and will properly invoke ’s destructor on scope exit, even though the destructor of is not virtual.

Conclusion

The Rule of Zero lets users do more by writing less. Use it as a guideline when you can and apply the Rule of Five only when you have to.

There is almost no need to manage your own resources so resist the temptation to implement your own copy/assign/move construct/move assign/destructor functions.

Managed resources can be resources inside your class definition or instances of your classes themselves. Refactoring the code around standard containers and class templates like or will make your code more readable and maintainable.

Help is on its way [N3949] in the form of and , to extend the way you can enforce the rule.

Acknowledgements

Thanks to Peter Sommerlad, Jonathan Wakely and Ric Parkin for their very helpful comments on drafts of this material.

References

[Abrahams1] Dave Abrahams, Implicit Move Must Go. http://cpp-next.com/archive/2010/10/implicit-move-must-go

[Abrahams2] Dave Abrahams. w00t w00t nix nix http://cpp-next.com/archive/2011/02/w00t-w00t-nix-nix

[Fernandes1] Martinho Fernandes. Rule of Zero. http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html

[Koenig/Moo1] Andrew Koenig/Barbara Moo. C++ Made Easier: The Rule of Three: http://www.drdobbs.com/c-made-easier-the-rule-of-three/184401400

[Koenig/Moo2] Andrew Koenig/Barbara Moo. Ruminations in C++. Destructors are special. ISBN-10 0-201-42339-1

[N1316] Draft for C++03. Standard for Programming Language C++http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2001/n1316/

[N3153] Dave Abrahams. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3153.htm

[N3174] Bjarne Stroustrup. To move or not to move. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3174.pdf

[N3201] Bjarne Stroustrup. Moving right along. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3201.pdf

[N3203] Jens Maurer. Tightening the conditions for generating implicit moves http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3203.htm

[N3242] Draft for C++11. Standard for Programming Language C++http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf

[N3797] Draft for C++14. Standard for Programming Language C++http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf

[N3839] Walter Brown. Proposing the Rule of Five, v2. http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3839.pdf

[N3949] Peter Sommerlad/A L Sandoval. Scoped Resource – Generic RAII wrapper for the Standard Library http://isocpp.org/files/papers/N3949.pdf

[Sommerlad1] Peter Sommerlad, Simpler C++ with C++11/14,http://wiki.hsr.ch/PeterSommerlad/files/MeetingCPP2013_SimpleC++.pdf

[Stroustrup1] Bjarne Stroustrup. The C++ Programming Language, Fourth Edition, section 17.2.5. ISBN-13 978-0-321-56384-2

[Sutter1] Herb Sutter/Andrei Alexandrescu. C++ Coding Standards. Ch 50. ISBN-13 978-0-321-11358-0