In some code I am reviewing I ran into a case where Clang and Gcc disagree. After looking around awhile I cant seem to figure out who is right.
Disclaimer: I know there is a better Singleton pattern, but this is the one used in the code.
Notes:
gcc 7.4.0 on Ubuntu (No error)
clang 6.0.0 on Ubuntu (Throws error)
Difference appears to exist for all post C++11 ISO versions, but I did not try earlier.
foo.hh
#include "sing.hh"
class Foo {
public:
Foo();
~Foo();
static Foo *getSingleton(){
return singleton<Foo>::instance();
}
};
foo.cc
include "foo.hh"
//removing this line results in the error for clang disappearing
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr;
int main(){};
sing.hh
template<typename T>
class singleton{
typedef T *(*GetInstance)(void);
public:
static GetInstance instance;
};
Results:
$ clang++ foo.cc
foo.cc:3:56: error: explicit specialization of 'instance' after instantiation
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr;
^
./foo.hh:10:32: note: implicit instantiation first required here
return singleton<Foo>::instance();
^
1 error generated.
$ g++ foo.cc <- No Errors
Neither compiler is technically wrong. The code is invalid, but C++ implementations are not required to give a diagnostic message about this type of error.
Standard [temp.expl.spec]/6 says (emphasis mine):
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
You can fix this by declaring the explicit specialization immediately after the definition of singleton
in sing.hh:
struct Foo;
template<> singleton<Foo>::GetInstance singleton<Foo>::instance;
Or, if you want all specializations to initialize as null pointers, you can just define the member of the general class template, again probably in sing.hh. Then there's no need for explicit specializations, unless you want a different initializer for some certain type(s).
template<typename T>
typename singleton<T>::GetInstance singleton<T>::instance = nullptr;