C++: Conditional and templated copy/move constructors & assignment operators.

The current (C++14) standard declares copy and move constructors as non-template constructors:

12.82: A non-template constructor for class X is a copy constructor …

12.83: A non-template constructor for class X is a move constructor …

Likewise sections 12.817 and 12.819 define copy and move assignment operators in a similar manner.

Unfortunately it means that generic (template) or conditional copy/move constructors and assignment operators require a little bit of trickery. Consider the following:

template <typename T>
class C {
public:
  C() { ... }
  C(C &&other) { ... }
};

To conditionally remove the move constructor based on a type trait (e.g. using std::enable_if to employ SFINAE) the move constructor would have to be a template method, but that constructor would no longer be a move constructor, per se.

One way to sidestep that is to have a non-template move constructor and to conditionally delete that move constructor. To do so, we’d add a base class that handles the moving, and we use multiple inheritance and partial template specialization to conditionally delete that move constructor:

template <bool b>
struct move_ctor_deleter {};
template <>
struct move_ctor_deleter<false> {
    move_ctor_deleter(move_ctor_deleter&&) = delete;
};

template <typename T>
class B {
public:
 B() { ... }
 B(B &&other) { ... }
};

template <typename T>
class C : public B<T>, move_ctor_deleter<condition> {
public:
};

Similarly, generic move constructors can now be added to C, while C would still correctly remain move-constructible:

template <typename T>
class C : public B<T>, move_ctor_deleter<condition> {
public:
  template <typename S>
  C(C<S> &&) { ... }
};

 

Of course, this technique trivially extends to copy constructors and copy/move assignment operators.