LambdaScientist
LambdaScientist

Reputation: 435

Clang and Gcc disagree on explicit specialization after instantiation

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:

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

Answers (2)

aschepler
aschepler

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

Constantinos Glynos
Constantinos Glynos

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

Related Questions