How to fix a purported lack of an “explicit instantiation declaration” when compiling a CRTP Singleton with Clang?

一曲冷凌霜 提交于 2019-12-07 03:34:12

问题


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?


回答1:


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



回答2:


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>




回答3:


Apparently, the explicit instantiation declaration should be of the form

template <> Singleton * SingletonBase< Singleton >::instance_;


来源:https://stackoverflow.com/questions/39420569/how-to-fix-a-purported-lack-of-an-explicit-instantiation-declaration-when-comp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!