c++c++11instantiationexternexplicit-instantiation

Error using `extern template` in the presence of an explicit specialization of a class template member function


Consider a class template S:

[s.hpp]

template <typename>
struct S
{
    void f() { /* ... */ }
    void g() { /* ... */ }
};

This class template also comes with a source file containing a specialization of S<int>::g():

[s.cpp]

template <>
void S<int>::g() { /* ... */ }

S is instantiated a lot of times as S<int>. In order to speed up compilation times by avoiding multiple instantiations of S<int>, I want to introduce an explicit instantiation declaration (extern template) in the header file s.hpp, and a corresponding explicit instantiation definition in the source file s.cpp:

[s.hpp] (modified)

template <typename>
struct S
{
    void f() { /* ... */ }
    void g() { /* ... */ }
};

extern template struct S<int>;

[s.cpp] (modified)

template <>
void S<int>::g() { /* ... */ }

template struct S<int>;

Doing so, however, results in the following compilation error:

<source>:11:14: error: explicit specialization of 'g' after instantiation
void S<int>::g() { /* ... */ }
             ^
<source>:8:23: note: explicit instantiation first required here
extern template class S<int>;
                      ^

I presume that the error happens because extern template is still considered an instantiation, even thought it is just a declaration.

How can I achieve a possible compilation speedup by providing an extern template declaration in the client-facing header for S, while still retaining an explicit specialization of S<int>::g in the source file?


Solution

  • Declare the explicit specialisation in the header as well.

    template <typename>
    struct S
    {
        void f() { /* ... */ }
        void g() { /* ... */ }
    };
    
    template <>
    void S<int>::g();
    
    extern template struct S<int>;
    

    To distill the rules: a declaration of an explicit specialization must appear before an entity is the subject of explicit instantiation - definition or declaration. Because the explicit instantiation of S<int> recursively constitutes the same for S<int>::g, you need to declare the specialization in advance.

    But let's just be thankful we aren't on fire.