Distributing the source into three files just confuse the matter: the preprocessing makes one compilation unit and the behavior just depend on the content of the CU and not in how many files it was distributed.
I think that you are surprised that in this case
#include <iostream>
template<class T> void foo(T); // A
template<> void foo(int*); // 1
template<class T> void foo(T*); // B
template<class T> void foo(T x)
{ std::cout << "T version." << std::endl; }
template<> void foo(int *i) // 2
{ std::cout << "int* version." << std::endl; }
template<class T> void foo(T *x)
{ std::cout << "T* version" << std::endl; }
int main(int argc, char** argv) {
int *p;
foo(p);
}
you get int* version
. This is the expected behavior. While (1) does declare a specialization of template <typename T> void foo(T)
, (2) isn't the definition of that specialization. (2) defines and declares a specialization of template<class T> void foo(T*);
which is then called in main()
. This will happen if you gives the three declarations before the three definitions in whatever you put the declarations and definitions. The definition (2) will always see the declaration template<class T> void foo(T*);
and thus be a specialization of it.
When a specialization for a function template is declared or defined and it could be a specialization for several function templates (like here (2) can be a specialization of the two overloads A and B, they just need to be declared), it it a specialization of the "more specialized" one. You can see the precise definition of "more specialized" in the standard section 17.5.5.2, but it is quite easy to see that B is a better match than A for (2) and thus (2) is a specialization of (B). (1) declares a specialization of (A) because when (1) is declared, (B) hasn't been seen yet. If you wanted to give the definition of (1) after (B) has been seen, you'd have to write
template <> void foo<int*>(int*) // definition for (1)
{ std::cout << "foo<int*>(int*)\n"; }
You could also be explicit when defining (2):
template<> void foo<int>(int *i) // 2 alternate
{ std::cout << "int* version." << std::endl; }
(but obviously giving (2) and this alternate version in the same CU will gives you an error).
You can be also explicit also when calling the functions:
foo(p); // call (2)
foo<int>(p); // call (2)
foo<int*>(p); // call (1)