Reputation: 435
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
Upvotes: 3
Views: 1705
Reputation: 72473
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;
Upvotes: 4
Reputation: 3196
From this answer here and the cpp-reference here.
Explicit specialization may be declared in any scope where its primary template may be defined [...].
Explicit specialization has to appear after the non-specialized template declaration.
Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs
If the explicit specialization was in the sing.cpp
file, then neither compiler would complain. Alternatively, you can use forward declaration to do the following and both clang and gcc would be happy.
#include <iostream>
template<typename T>
struct singleton
{
typedef T *(*GetInstance)(void);
static GetInstance instance;
};
template<>
singleton<struct Foo>::GetInstance singleton<struct Foo>::instance = nullptr;
struct Foo
{
static Foo *getSingleton()
{
return singleton<Foo>::instance();
}
};
int main()
{
}
Online code example: https://rextester.com/SPZLS83155
Upvotes: 1