Consider the following code:
#include <iostream>
// void foo(int* t ) { std::cout << "variant int* t, non-template\n"; }
template<typename T> void foo(T t ) { std::cout << "variant T t\n"; }
template<> void foo(int* t ) { std::cout << "variant int* t\n"; }
template<typename T> void foo(T* t ) { std::cout << "variant T* t\n"; }
int main()
{
foo( new int{1} );
return 0;
}
If the non-template version is uncommented, then it is preferred in the overload resolution, as expected. Let's leave it commented.
Curiously, whether the variant with T* t or int* t is chosen in overload resolution depends on the order of declaration.
The output is variant T* t with the lines
template<> void foo(int* t ) { std::cout << "variant int* t\n"; }
template<typename T> void foo(T* t ) { std::cout << "variant T* t\n"; }
The output is variant int* t with the swapped lines
template<typename T> void foo(T* t ) { std::cout << "variant T* t\n"; }
template<> void foo(int* t ) { std::cout << "variant int* t\n"; }
So it seems that the order of declaration matters. But shouldn't the most special specialization be preferred? What rule applies here?
tldr
There are two primary templates here and only primary templates take part in overload resolution. For the call foo(new int{1}), the primary template #2 is a better match. In the second snippet, the second template #2 has a specialization which by definition is more specialized than the primary template #2 and so is used.
Here we consider the case when we have the order as:
template<typename T> void foo(T t ) //#1 primary template
{
std::cout << "variant T t\n";
}
//this specializes the above #1
template<> void foo(int* t )
{
std::cout << "variant int* t\n";
}
template<typename T> void foo(T* t ) //#2 primary template
{
std::cout << "variant T* t\n";
}
The important thing to note is that:
a) only primary templates take part in overload resolution. This means that only #1 and #2 take part in overload resolution. This means that for the call foo( new int{1} ), the second template #2 is selected as it is a better match than the other.
b) Note that in this case, the second primary template #2 doesn't have any specialization so the #2 is used and we get the output as variant T*. The specialization that you've provided is for #1 which can't be used because #1 is worst match than #2.
Here we consider the declaration order as:
template<typename T> void foo(T t ) //#1 primary template
{
std::cout << "variant T t\n";
}
template<typename T> void foo(T* t ) //#2 primary template
{
std::cout << "variant T* t\n";
}
//this specializes the above #2
template<> void foo(int* t )
{
std::cout << "variant int* t\n";
}
In this case again only the primary templates #1 and #2 take part in overload resolution. And the second primary template #2 is a better match than the first #1.
But the difference is that this time there exists a specialization for the selected #2. And since a specialization is more specialized than the primary template, that is used and so we get the output as variant int*
There are (only) two templates here:
template<typename T> void foo(T t ) { std::cout << "variant T t\n"; }
template<typename T> void foo(T* t ) { std::cout << "variant T* t\n"; }
The line
template<> void foo(int* t ) { std::cout << "variant int* t\n"; }
is a specialization of a template, not itself a template. If it is after the second template it is a specialization of the second template, but if it is between, it specializes the first template.
When you call foo(int *) in main, either template can match, but the second one matches better so that is the one that is used. If (and only if) the specialization is a specialization of that template, it will be used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With