首页 > 解决方案 > 无复制构造函数的简单编译时间检查

问题描述

制作无副本类型的一个著名习惯用法是创建一个基类

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

并由此衍生,就像这样

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

这将使以下内容无法编译

Foo f;
Foo f2 = f;

但是我该如何执行呢?任何派生类都可以执行以下操作

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

这是完全合法的,但没有任何意义,我现在有一个既可复制又不可复制的类型(通过它的基类)。

我该如何避免这种情况?

标签: c++c++11

解决方案


这是 C++。在模板元编程的世界里,几乎一切皆有可能。如果我们创建NoCopy一个 CRTP 基础,我们可以在其析构函数中添加静态断言。

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?");
   }
};

这是您的代码,适用于现场示例

不过,这并非没有代价,因为现在该类不是可轻易破坏的,因此也不是任何派生自它的类。能不能接受就看你自己了。


经过进一步思考,如果您只提供一种初始化类的方法,则必须引用和调用默认构造函数。所以静态断言可以移到那里,并且类型恢复到可以简单地破坏:

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;
};  

正如这个现场示例所示,静态断言的触发方式相同。


推荐阅读