Search code examples
c++singletoncrtp

Implementing Singleton with CRTP


After reading this answer I have tried implementing some simple CRTP usage. I figured I'd try to implement the Singleton (yes, I know - it's just for practice and research) pattern, given the fact that the linked answer kind of already does it... except for the fact that it does not compile.

The quoted code is as follows:

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

Which I then 'modernized' to:

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

I then tried to create a reference to the instance:

auto& x = A::get_instance();

which obviously did not compile.

It's worth mentioning that I get very similar error messages, notably:

note: 'A::A()' is implicitly deleted because the default definition would be ill-formed: class A : public Singleton<A>.

Obviously, the second snippet of code cannot compile, since we deleted the default constructor and try to use it with new T in the get_instance method.

What surprises me is that the first snippet doesn't compile either, with similar error messages. Does the linked answer has a mistake? How would I implement a generic base class / interface for Singletons using CRTP?


Solution

  • Here's mods to the "modernized" snippet:

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

    Summary of changes from snippet:

    • protected default constructor in singleton
    • private nested struct to access derived class's protected constructor
    • protected constructor in derived class to prevent instantiation

    Also, no need to delete constructors that are implicitly deleted by adding default ctor implementation to Singleton class.

    Not so small as Richard Hodges' example, but the static instance member makes it easy to add a delete_instance() method for use in automated unit tests.