//(1)
template <class T>
void f(T) {}
//(2)
template <class T>
void f(T*) {}
//(3)
template <>
void f<>(int*) {}
//(4)
void f(int*) {}
int main()
{
int* p;
f(p); // which function will be called?
return 0
}
I already know the behavior:
It bothers me that order matters (because it adds uncertainty and guess work when I write code) especially that I know that if those were class specializations then the order would not matter and the most specialized class with be called by compiler.
Can someone explain to me how this is working? and what are the rules that the compiler follow to find appropriate function specializations?
The fundamental rule is actually reasonably simple: functions and function templates participate in overload resolution. Explicit function template specialisations don't.
So, for the call f(p), the compiler does overload resolution to choose between (1), (2), and (4). (3) is never considered for overload resolution.
Overload resolution chooses a single function or function template. After it's complete, if it chose a function template, then will specialisations of that template be considered.
Let's analyse different combinations of the declarations you've provided.
If (4) is present, it will be chosen unambiguously. When everything else is equal, non-template functions are a better overload match than function templates.
Let's say we have just (1), (2), and (3), in this order. Overload resolution chooses between (1) and (2). (2) is better, because it's more specialised. So the function template (2) is chosen by overload resolution.
Then, the compiler looks whether the template has any specialisations. It does, in fact—the specialisation (3), for T = int. So this specialisation is chosen.
Now, let's say the order is (1), (3), (2). Again, overload resolution chooses between (1) and (2) (remember specialisations, which (3) is, never participate in overload resolution). (2) is chosen again. This time, (3) is a specialisation of (1), for T = int*. That is so because (2) did not yet exist when (3) was declared, so there was no (2) to specialise. But (1) was not chosen by overload resolution, so it's not considered.
The order (2), (3), (1) is the same as (1), (2), (3). The order (2), (1), (3) is the same as (1), (3), (2). The relative order of (1) and (2) doesn't matter—it's the placement of (3) which controls which template it will specialise.
It depends if overload (4) is present.
... which is due to a special rule in overload resolution: Functions that aren't specializations of some template are preferred over function template specializations if these are otherwise indistinguishable by the rules of overload resolution. E.g.
template <typename T>
void f(T); // A
void f(int); // B
f(4); // Calls B - Conversions of 4 to both parameter types are equivalent
This also applies to your sample: Since the parameter types of f<int> (1), f<int*> (2) and f (4) are identical, (4) must be chosen.
Once (4) is gone, partial ordering kicks in: It determines that (2) is more specialized, because
some arbitrary type U cannot be matched by T* (U isn't a pointer), while
some arbitrary pointer type V* can be matched by T (since T will be simply deduced as V*).
Thus the specialization f<int> from (2) is a better match.
It depends on the order of declaration of (1) (2) and (3)
You declare two function template overloads of the name f. But the explicit specialization you declare can appertain to both! The template argument is deduced, and it can be deduced for both f(T) and f(T*), since they can both take a pointer to int.
template <class T> void f(T) {} // (1)
// Specializes f<int*> from (1), (2) not yet declared
template <> void f<>(int*) {}
template <class T> void f(T*) {} // (2)
Or
template <class T> void f(T) {} // (1)
template <class T> void f(T*) {} // (2)
// Specializes f<int> from (2)
template <> void f<>(int*) {}
In the latter case, (2) is chosen because it's more specialized than (1) (according to the rules of partial ordering). You can make the specialization unambiguous by providing the template arguments explicitly, rather than leaving them unspecified:
template <>
void f<int>(int*) {} // Can only specialize (2)
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