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?
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 singletonprivate
nested struct to access derived class's protected constructorprotected
constructor in derived class to prevent instantiationAlso, 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.