Trivial compile time check for no copy constructor

  • A+
Category:Languages

A well-known idiom for making no-copy types is to create a base class

struct NoCopy {    NoCopy(){}    NoCopy(const NoCopy&) = delete;    NoCopy& operator=(const NoCopy&) = delete;   }; 

And derive from this, like so

struct Foo : NoCopy {     Foo(){} }; 

Which will make the following fail to compile

Foo f; Foo f2 = f; 

But how do I enforce this? Any derived class can do the following

struct Foo2 : NoCopy {     Foo2(){}     Foo2(const Foo2&){} }; 

Which is perfectly legal but makes no sense, I have now a type which is both copyable and also not copyable (through its base class).

How do I avoid this?

 


This is C++. In the world of template meta-programming almost anything is possible. If we make NoCopy a CRTP base, we can add static assertions in its destructor.

template<class C> struct NoCopy {    NoCopy(){}    NoCopy(const NoCopy&) = delete;    NoCopy& operator=(const NoCopy&) = delete;    ~NoCopy() noexcept {        static_assert(std::is_base_of<NoCopy, C>::value, "CRTP not observed");        static_assert(!std::is_copy_constructible<C>::value, "A non-copyable copyable class? Really?");    } }; 

Here's your code, adapted for a live example.

That's not without a price though, since now the class is not trivially destructible, and as such neither will be any class that derives from it. Whether or not it's acceptable is up to you.


Upon further reflection, if you provide only a single way to intiialize your class, then the default constructor has to be referred to and called. So the static assertion can be moved there, and the type is back to being trivially destructible:

template<class C> struct NoCopy {    NoCopy() noexcept {        static_assert(std::is_base_of<NoCopy, C>::value, "CRTP not observed");        static_assert(!std::is_copy_constructible<C>::value, "A non-copyable copyable class? Really?");    }    NoCopy(const NoCopy&) = delete;    NoCopy& operator=(const NoCopy&) = delete; };   

The static assertion fires just the same, as this live example shows.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: