0

I wanted to check that the extern keyword did infact prevent class code being generated in the translation unit:

template<class> struct always_false : std::false_type {};

template <typename T> class A{
    static_assert(always_false<T>::value, "If this fires, 'A' is instantiated");
};

extern template class A<int>; //error: static assertion failed: If this fires, 'A' is instantiated|

int main(){
//    A<int> f;
}

Why is it that the previous code still produces an error from the static_assert if this is my only source file? As far as I understand from the explicit use of extern this should prevent any production of code for the class A<int> and the linker takes care of finding a later explicit instantiation definition (in the translation unit for which the code is actually written) to match any use of A<int> with.

However it seems that the explicit instantiation declaration is generating code in this translation unit itself as indicated by the compilation error. If I comment out extern template class A<int> everything works fine. I was using GCC 4.9.2. but it appears clang 3.5.1 throws this error too.

Alternatively this also kicks up the same assert error:

template<class> struct always_false : std::false_type {};

template <typename T> class A{
public:
    void test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }
};

extern template void A<int>::test();

int main(){
    A<int> a;
    a.test();
}

Here I would've expected the member function A<int>::test() to not even be instantiated and again wait until linking before "finding" code for the function, but it looks like the code is generated in the same translation unit. However if I take out the static_assert:

template <typename T> class A{
public:
    void test() { }
};

extern template void A<int>::test();

int main(){
    A<int> a;
    a.test();
}

Then I get the error I'm expecting, indicating that A<int>::test() is not instantiated and there was a linker error:

**undefined reference to `A<int>::test()'|**

Why would the static_assert throw an error if test() was never instantiated?

AntiElephant
  • 1,227
  • 10
  • 18

1 Answers1

3

Your premise is wrong. extern template prevents object code generation for function templates (including member functions of class templates), but it doesn't prevent the instantiation of the class bodies.

Edit: To answer the updated question: the member function is defined inline in the class, so the compiler will still instantiate it so that it can inline it if necessary. If you define the function out of line, you will not get an error (tried GCC 5.2.0 via godbolt).

#include <type_traits>

template<class> struct always_false : std::false_type {};

template <typename T> class A{
public:
    void test();
};

template <typename T>
void A<T>::test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }

extern template void A<int>::test();

int main(){
    A<int> a;
    a.test();
}
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Fair enough if that is the case. I'm reading 16.1.5 of C++ Primer 5th edition and it very much made it seem like `extern` prevents instantiation of the class bodies completely. I have edited in a little extra query to my OP though. – AntiElephant Nov 08 '15 at 23:56
  • @AntiElephant Updated answer. – Sebastian Redl Nov 09 '15 at 08:53
  • I see, [14.7.2][10] of N3337 outlines this inline exception too. So the call to `a.test()` is inlined which is what causes the `static_assert` to throw. However, after removing the assert from `test()`, it still produces the error of "undefined reference" even though the function was inlined at the call? Is that just for consistency? – AntiElephant Nov 09 '15 at 13:54
  • [11] - _An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation (14.7.1) in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed_. Combined with [14.7.2][10] this indicates the function is still implicitly instantiated because it's inlined, but because there was an explicit declaration with no corresponding definition it still throws a linker error. To be quite honest I find this quite odd behaviour. – AntiElephant Nov 09 '15 at 17:14