Why does the compiler instantiate templates while I ask him not to?

Viewed 99

I would like to prevent the compiler from implicitly instantiating some template with extern template.

The following snippets works as expected (the static_assert does not trigger):

template<typename T>
void f() {
    static_assert(sizeof(T) == 0, "f()");
};

extern template void f<int>();

void g() {
    f<int>();
}

But with this one it seems that the compiler tries to instantiante the template function, as the static_assert does trigger:

struct S {
    template<typename T>
    void f() {
        static_assert(sizeof(T) == 0, "S::f()");
    } 
};

extern template void S::f<int>();

void g() {
    S s;
    s.f<int>();
}

with this one, the static_assert also triggers whereas I would expect that it does not:

template<typename T>
struct S {
    S(){};
    static_assert(sizeof(T) == 0, "S");
};

extern template struct S<int>;

void g() {
    S<int> s;
}

In my actual cases, I would like to speedup compilation times, but I observe that the compilation of translation units where there is an extern template ..., the symbols related to these templates does not appears in the .o file (looking with nm), but they are actually compiled... (I check that through the observation of significant compilation times with clang's -ftime-trace and with templight++).

Why does extern template does not seems to work as expected?

Thanks!

1 Answers

Explicit instantiation declarations can’t suppress all instantiation: a class’s members must still be known to use an object of that type, after all, and inline functions must have their definitions known in order to be inlined.

This example is just both of those in quick succession: the class template can’t “protect” the member function template, and that member function template is inline because it’s defined in its class.

The latter rule is a bit arbitrary: it was once considered necessary for ODR reasons, but the modern understanding is that the “merging” of the whole class (template) definition is sufficient. Accordingly, C++20 removes that implicit inline in modules, where it interacts badly with linkage rules.

Related