We're using the curiously recurring template pattern to implement singletons. However, with recent Clang versions we're getting a -Wundefined-var-template warning. The suggested fix to which is to add an "explicit instantiation declaration".
I attempted to do this, but then I get errors about "explicit specialization after instantiation" in the compilation unit where the definition of the singleton template class member variable is.
What's the appropriate construct to fix the issue highlighted by this warning?
Simplified Details (much of the logic has been removed, to make a MCVE):
SingletonBase.hh:
template < class T > class SingletonBase {
public:
static T * get_instance() {
if ( ! instance_ ) {
instance_ = T::create_singleton_instance();
}
return instance_;
}
private:
static T * instance_;
};
Singleton.hh:
#include "SingletonBase.hh"
class Singleton : public SingletonBase< Singleton > {
friend class SingletonBase< Singleton >;
public:
int do_stuff(int v) { return v+2; }
private:
static Singleton * create_singleton_instance() {
return new Singleton;
}
};
Singleton.cc:
#include "Singleton.hh"
template <> Singleton * SingletonBase< Singleton >::instance_( nullptr );
When we compile with a recent version of clang (3.9.0; but not with clang 3.7), we get an warning when compiling files other than Singleton.cc. (with -std=c++11 and -Werror)
In file included from OtherFile.cc:2:
In file included from ./Singleton.hh:2:
./SingletonBase.hh:5:16: warning: instantiation of variable 'SingletonBase<Singleton>::instance_' required here, but no definition is available [-Wundefined-var-template]
if ( ! instance_ ) {
^
OtherFile.cc:5:25: note: in instantiation of member function 'SingletonBase<Singleton>::get_instance' requested here
return Singleton::get_instance()->do_stuff(4);
^
./SingletonBase.hh:11:18: note: forward declaration of template entity is here
static T * instance_;
^
./SingletonBase.hh:5:16: note: add an explicit instantiation declaration to suppress this warning if 'SingletonBase<Singleton>::instance_' is explicitly instantiated in another translation unit
if ( ! instance_ ) {
^
1 error generated.
I added the following line to the end of Singleton.hh, as it's what I'm lead to believe the explicit instantiation declaration syntax should be.
extern template Singleton* SingletonBase< class Singleton >::instance_;
While that fixes issues with compiling OtherFile.cc, it results in a new error when compiling Singleton.cc
Singleton.cc:3:57: error: explicit specialization of 'instance_' after instantiation
template <> Singleton * SingletonBase< Singleton >::instance_( nullptr );
^
./Singleton.hh:14:66: note: explicit instantiation first required here
extern template Singleton* SingletonBase< class Singleton >::instance_;
^
1 error generated.
What should I be doing here to fix these warnings/errors? Is there a more appropriate syntax for the explicit instantiation declaration that I'm not understanding?
The simplest fix is to define instance_
in SingletonBase.hh:
template < class T > class SingletonBase {
public:
static T * get_instance() {
if ( ! instance_ ) {
instance_ = T::create_singleton_instance();
}
return instance_;
}
private:
static T * instance_;
};
template <typename T>
T* SingletonBase<T>::instance_ = nullptr;
However, I don't see the point of SingletonBase
if you are going to rely on T::create_singleton_instance()
to create the instance. You might as well implement get_instance()
in the derived class.
Using a CRTP to implement the singleton pattern makes sense only if the base class can construct an instance of the derived class using the default constructor.
template < class T > class SingletonBase {
public:
static T& get_instance() {
static T instance_;
return instance_;
}
private:
};
Further reading: How to implement multithread safe singleton in C++11 without using <mutex>
I would recommend this implementation of a singleton instead:
template<class T>
struct SingletonBase {
static T& get_instance() {
static T instance;
return instance;
}
};
It's thread safe and remove your warning.
If you want, you can keep your create_singleton_instance
:
template<class T>
struct SingletonBase {
static T& get_instance() {
static T instance{T::create_singleton_instance()};
return instance;
}
};
And changing the function implementation to:
static SomeClass create_singleton_instance() {
return {};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With