Copy constructors were traditionally ubiquitous in C++ programs. However, I'm doubting whether there's a good reason to that since C++11.
Even when the program logic didn't need copying objects, copy constructors (usu. default) were often included for the sole purpose of object reallocation. Without a copy constructor, you couldn't store objects in a std::vector
or even return an object from a function.
However, since C++11, move constructors have been responsible for object reallocation.
Another use case for copy constructors was, simply, making clones of objects. However, I'm quite convinced that a .copy()
or .clone()
method is better suited for that role than a copy constructor because...
Copying objects isn't really commonplace. Certainly it's sometimes necessary for an object's interface to contain a "make a duplicate of yourself" method, but only sometimes. And when it is the case, explicit is better than implicit.
Sometimes an object could expose several different
.copy()
-like methods, because in different contexts the copy might need to be created differently (e.g. shallower or deeper).In some contexts, we'd want the
.copy()
methods to do non-trivial things related to program logic (increment some counter, or perhaps generate a new unique name for the copy). I wouldn't accept any code that has non-obvious logic in a copy constructor.Last but not least, a
.copy()
method can be virtual if needed, allowing to solve the problem of slicing.
The only cases where I'd actually want to use a copy constructor are:
- RAII handles of copiable resources (quite obviously)
- Structures that are intended to be used like built-in types, like math vectors or matrices -
simply because they are copied often andvec3 b = a.copy()
is too verbose.
Side note: I've considered the fact that copy constructor is needed for CAS, but CAS is needed for
operator=(const T&)
which I consider redundant basing on the exact same reasoning;
.copy()
+operator=(T&&) = default
would be preferred if you really need this.)
For me, that's quite enough incentive to use T(const T&) = delete
everywhere by default and provide a .copy()
method when needed. (Perhaps also a private T(const T&) = default
just to be able to write copy()
or virtual copy()
without boilerplate.)
Q: Is the above reasoning correct or am I missing any good reasons why logic objects actually need or somehow benefit from copy constructors?
Specifically, am I correct in that move constructors took over the responsibility of object reallocation in C++11 completely? I'm using "reallocation" informally for all the situations when an object needs to be moved someplace else in the memory without altering its state.