You have to think of an explicit specialization as a function declaration. Just like if you had two overloaded functions (non-templated), if only one declaration can be found before you try to make a call to the second version, the compiler is going to say that it cannot find the required overloaded version. The difference with templates is that the compiler can generate that specialization based on the general function template. So, why is it forbidden to do this? Because the full template specialization violates the ODR when it is seen, since, by that time, there already exists a template specialization for the same type. When a template is instantiated (implicitly or not), the corresponding template specialization is also created, such that later use (in the same translation unit) of the same specialization will be able to reuse the instantiation and not duplicate the template code for every instantiations. Obviously, the ODR applies just as well to template specializations as it applies elsewhere.
So, when the quoted text says "no diagnostic is required", it just means that the compiler is not required to provide you with the insightful remark that the problem is due to an instantiation of the template occurring sometime before the explicit specialization. But, if it doesn't do that, the other option is to give the standard ODR violation error, i.e., "multiple definitions of 'Foo' specialization for [T = int]" or something like that, which wouldn't be as helpful as the more clever diagnostic.
RESPONSE TO EDIT
1) Although the saying goes that all template function definitions (i.e. implementation) must be visible at the point of instantiation (such that the compiler can substitute the template argument(s) and instantiate the function template). However, implicit instantiation of the function template only requires that the declaration of the function be available. So, in your case, splitting it into two translation units works, because it does not violate ODR (since in that TU, there is only one declaration of Foo<int>
), the declaration if Foo<int>
is available at the implicit instantiation point (through Foo<T>
), and the definition of Foo<int>
is available to the linker within TU B. So, no one has argued that this second example is "not supposed to work", it works as it is supposed to. Your question is about a rule that applies within one translation unit, don't counter the arguments by saying that the error doesn't occur when you split it into two TUs (especially when it clearly should work fine in two TUs, according to the rules).
2) In your first example, either there will be an error because the compiler cannot find the general function template (the non-specialized implementation) and thus cannot instantiate Foo<int>
from the general template. Or, the compiler will find a definition for the general template, use it to instantiate Foo<int>
, and then throw an error because a second template specialization Foo<int>
is encountered. You seem to think that the compiler will find your specialization before it gets to it, it doesn't. C++ compilers compile the code from top to bottom, they don't go back and forth to substitute stuff here and there. When the compiler gets to the first use of Foo<int>
, it sees only the general template at that point, assumes there will be an implementation of that general template that can be used to instantiate Foo<int>
, it is not expecting a specialized implementation for Foo<int>
, it is expecting and will use the general one. Then, it sees the specialization and throws the error, because it already had made its mind that the general version was to be used, so, it does see two distinct definitions for the same function, and yes, it does violate ODR. It's as simple as that.
WHY OH WHY!!!
The 2 TU case has to work because you should be able to share template instantiations between TUs, that's a feature of C++, and a useful one (in case when you have a small number of possible instantiations, you can pre-compile them).
The 1 TU case cannot be allowed because declaring something in C++ tells the compiler "there is this thing defined somewhere". You tell the compiler "there is a general definition of the template somewhere", then say "I want to use the general definition to make the function Foo<int>
", and finally, you say "whenever Foo<int>
is called, it should use this special definition". That's a flat out contradiction! That's why the ODR exists and applies to this context, to forbid such contradictions. It doesn't matter whether the general definition "to-be-found" is not present, the compiler expects it, and it has to assume that it does exist and that it is different from the specialization. It cannot go on and say "ok, so, I'll look everywhere else in the code for the general definition, and if I cannot find it, then I will come back and 'approve' this specialization to be used instead of the general definition, but if I find it I will flag this specialization as an error". Nor can it go on and flatly ignore the desire of the programmer by changing code that clear shows intent to use the general template (since the specialization is not declared yet), for code that uses a specialization that appears later. I can't possibly explain the "why" any more clearly than that.
The 2 TU case is completely different. When the compiler is compiling TU A (that uses Foo<int>
), it will look for the general definition, fail to find it, assume that it will be linked-in later as Foo<int>
, and leaves a symbol placeholder. Then, since the linker will not look for templates (templates are NOT exportable, in practice), it will look for a function that implements Foo<int>
, and it doesn't care whether it is a specialized version or not. The linker is happy as long as it finds the same symbol to link to. This is so, because it would be a nightmare to do it otherwise (look up discussions on "exported templates") for both the programmers (not being able to easily change functions in their compiled libraries) and for the compiler vendors (having to implement this linking crazy scheme).