Is the Rule of 5 (for constructors and destructors) outdated?

The full name of the rule is the rule of 3/5/0.

It doesn't say "always provide all five". It says that you have to either provide the three, the five, or none of them.

Indeed, more often than not the smartest move is to not provide any of the five. But you can't do that if you're writing your own container, smart pointer, or a RAII wrapper around some resource.


The full rule is, as noted, the Rule of 0/3/5; implement 0 of them usually, and if you implement any, implement 3 or 5 of them.

You have to implement the copy/move and destruction operations in a few cases.

  1. Self reference. Sometimes parts of an object refer to other parts of the object. When you copy them, they'll naively refer to the other object you copied from.

  2. Smart pointers. There are reasons to implement more smart pointers.

  3. More generally than smart pointers, resource owning types, like vectors or optional or variants. All of these are vocabulary types that let their users not care about them.

  4. More general than 1, objects whose identity matters. Objects which have external registration, for example, have to reregister the new copy with the register store, and when destroyed have to deregister themselves.

  5. Cases where you have to be careful or fancy due to concurrency. As an example, if you have a mutex_guarded<T> template and you want them to be copyable, default copy doesn't work as the wrapper has a mutex, and mutexes cannot be copied. In other cases, you might need to guarantee the order of some operations, do compare and sets, or even track or record the "native thread" of the object to detect when it has crossed thread boundaries.


However, in normal code I don't see any reason to use user-defined constructors.

User provided constructor allows also to maintain some invariant, so orthogonal with rule of 5.

As for example a

struct clampInt
{
    int min;
    int max;
    int value;
};

doesn't ensure that min < max. So encapsulate data might provide this guaranty. aggregate doesn't fit for all cases.

when do you ever need a user-defined destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor?

Now about rule of 5/3/0.

Indeed rule of 0 should be preferred.

Available smart-pointers (I include container) are for pointers, collections or Lockables. But resources are not necessary pointers (might be handle hidden in an int, internal hidden static variables (XXX_Init()/XXX_Close())), or might requires more advanced treatment (as for database, an auto commit at end of scope or rollback in case of exceptions) so you have to write your own RAII object.

You might also want to write RAII object which doesn't really own resource, as a TimerLogger for example (write elapsed time used by a "scope").

Another moment when you generally have to write destructor is for abstract class, as you need virtual destructor (and possible polymorphic copy is done by a virtual clone).