Is CRTP version of NonCopyable pattern correct?

Viewed 100

There is a well-known pattern of NonCopyable mix-in. There is a CRTP version of it. It can be found in many libraries and many examples. But this code seems to behave strange:

#include <type_traits>

struct NonCopyable {
public:
    NonCopyable(const NonCopyable &) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;

protected:
    NonCopyable() = default;
    ~NonCopyable() = default;
};

template<typename T>
struct NonCopyableCRTP {
public:
    NonCopyableCRTP(const NonCopyableCRTP &) = delete;
    T& operator=(const T&) = delete;

protected:
    NonCopyableCRTP() = default;
    ~NonCopyableCRTP() = default;
};

struct Test : private NonCopyableCRTP<Test> {};

int main() {
    Test t, t2;

    static_assert(std::is_copy_assignable_v<Test>); // <--- what?
    t2 = t; // <--- this works!

    static_assert(!std::is_copy_assignable_v<NonCopyable>);
    static_assert(std::is_copy_assignable_v<NonCopyableCRTP<int>>);

    return 0;
}

CPPInsights shows what is going on. But I have a question - are all those CRTP examples of NonCopyable around the web broken? And how can we fix them? Should this line:

T& operator=(const T&) = delete;

be replaced with this one:

NonCopyableCRTP<T>& operator=(const NonCopyableCRTP<T>&) = delete;
0 Answers
Related