The ease of racing in orbit led to why this does not fit the details of the Standard. There may be others in the vicinity.
I will try to explain more simply what the standard wording means, and I hope I get it right and finally explain the linker errors (or the lack of error):
- What is the point of instantiating?
- How does the compiler choose specialization?
- What is needed at the time of instantiation?
- Why is the linker error?
1 / What is the instantiation point?
An instance point of a template function is the point at which it is called or called ( &std::sort<Iterator> ) with all template parameters specified (*).
template <typename T> void foo(T) { std::cout << typeid(T).name() << "\n"; } int main() { foo(1); }
It may be delayed, although it does not match the exact site of the call, for templates called from other templates:
template <typename T> void foo(T) { std::cout << typeid(T).name() << "\n"; } template <typename T> void bar(T t) { foo(t); }
This delay is very important because it allows you to record:
- core recursive patterns (i.e. patterns that apply to each other)
- custom majors
(*) Roughly speaking, there are exceptions, such as non-template methods of the template class ...
2 / How does the compiler choose specialization?
At the time of instantiation, the compiler should be able to:
- decide which basic template function to call
- and perhaps which of his specializations is causing
This old GotW shows specialization issues ... but in short:
template <typename T> void foo(T);
are overloads , and each generates a different family of possible specializations, the basis of which they are.
template <> void foo<int>(int);
is a specialization 1 and
template <> void foo<int*>(int*);
- specialization 2.
To allow a function call, the compiler first selects the best overload, ignoring the specialized templates, and then, if she selects a template function, check if she has any specialization that might be better applied.
3 / What is needed at the time of instantiation?
So, from the way the compiler resolves the call, we understand why the standard indicates that any specialization should be declared before its first instantiation point. Otherwise, it is simply not considered.
Thus, at the time of instantiation it was necessary to see:
- declaration of the underlying template function to be used
- announcement of the chosen specialization, if any
But what of the definition?
It is not necessary. The compiler assumes that it will either be provided later in the TU, or by the other TU as a whole.
Note: this loads the compiler, because it means that it must remember all the implicit instances that it encountered, and for which it could not emit the body of the function, so that when it finally encounters the definition, it can (finally ) emit all the necessary code for all the specializations that he encountered. It is interesting why this particular approach was chosen, as well as wondering why, even in the absence of an declaration, extern TU can end with undefined function bodies.
4 / Why is the linker error?
Since no definition is provided, gcc trusts you later, and simply issues a call to the unresolved character. If you manage to associate with another TU that provides this symbol, then everything will be fine, otherwise you will get a linker error.
Since gcc follows Itanium ABI , we can just see how it manages characters. It turns out that ABI does not make any difference in specialized specializations and implicit instances, therefore
cls.f( asd );
calls _ZN3cls1fIPKcEEvT_ (which expands as void cls::f<char const*>(char const*) ) and specialization:
template<> void cls::f( const char* ) { }
also creates _ZN3cls1fIPKcEEvT_ .
Note: it is not clear to me whether explicit specialization can get another distortion.