首页 > 解决方案 > 使用 CRTP 实现单例

问题描述

阅读此答案后,我尝试实现一些简单的 CRTP 用法。我想我会尝试实现单例(是的,我知道 - 它只是为了练习和研究)模式,因为链接的答案已经做到了......除了它没有编译的事实。

引用的代码如下:

template <class ActualClass> 
class Singleton
{
   public:
     static ActualClass& GetInstance()
     {
       if(p == nullptr)
         p = new ActualClass;
       return *p; 
     }

   protected:
     static ActualClass* p;
   private:
     Singleton(){}
     Singleton(Singleton const &);
     Singleton& operator = (Singleton const &); 
};
template <class T>
T* Singleton<T>::p = nullptr;

class A: public Singleton<A>
{
    //Rest of functionality for class A
};

然后我将其“现代化”为:

template <class T>
class Singleton {
public:
    Singleton()                              = delete;
    Singleton(const Singleton&)              = delete;
    Singleton(Singleton&&)                   = delete;
    Singleton& operator = (const Singleton&) = delete;
    Singleton& operator = (Singleton&&)      = delete;

    static T& get_instance() {
        if(!instance)
            instance = new T;
        return *instance;
    }

   protected:
     static inline T* instance = nullptr;
};

class A: public Singleton<A> {
    //Rest of functionality for class A
};

然后我尝试创建对实例的引用:

auto& x = A::get_instance();

这显然没有编译。

值得一提的是,我收到了非常相似的错误消息,特别是:

注意: 'A::A()' 被隐式删除,因为默认定义格式不正确:class A : public Singleton<A>

显然,第二段代码无法编译,因为我们删除了默认构造函数并尝试new Tget_instance方法中使用它。

令我惊讶的是,第一个片段也没有编译,并带有类似的错误消息。链接的答案有错误吗?我将如何使用 CRTP为 Singletons 实现通用基类/接口?

标签: c++singletoncrtp

解决方案


这是“现代化”片段的修改:

template <class T>
class Singleton {
public:
    Singleton& operator = (const Singleton&) = delete;
    Singleton& operator = (Singleton&&)      = delete;

    static T& get_instance() {
        if(!instance)
            instance = new T_Instance;
        return *instance;
    }

protected:
    Singleton() {}

private:
    struct T_Instance : public T {
        T_Instance() : T() {}
    };

    static inline T* instance = nullptr;
};

class A : public Singleton<A> {
protected:
    A() {}
};

int main()
{
    auto& x = A::get_instance();
}

片段的更改摘要:

  • protected单例中的默认构造函数
  • private嵌套结构访问派生类的受保护构造函数
  • protected派生类中的构造函数以防止实例化

此外,不需要delete通过将默认 ctor 实现添加到 Singleton 类来隐式删除的构造函数。

不像Richard Hodges的例子那么小,但是静态instance成员可以很容易地添加 delete_instance() 方法以用于自动化单元测试。


推荐阅读