In the first segment code all is ok. 2 template functions make an overload set, second wins and instanced by specialization.
template <typename T> void foo(T) {std::cout << "1";}
template <typename T> void foo(T*) {std::cout << "2";}
template <> void foo(int*) {std::cout << "3";}
int x;
foo(&x);
But if I make a little changes, it's behaviour is out of my mind:
template <typename T> void foo(T) {std::cout << "1";}
template <typename T> void foo(T*) {std::cout << "2";}
template <> void foo<int*>(int*) {std::cout << "3";}
int x;
foo(&x);
Suddenly specialization is relate to first function only. Explain, how it works, please
I have tried some test and cannot fuuly understand how it works after all
With template classes, we have specialization and partial specialization of classes.
With template functions, we have full specialization and overloading of functions.
These are often used to solve similar problems, but they work differently.
here, C++ tries to do magic for you. Your
template<> void foo(int*)is a specialization, and you have asked for the language to auto-detect which function template it is a specialization for. It picks #2. The process which it picks it involves faking a function call and then following a bunch of obtuse rules. It usually gets it right.here you have said "no, I will tell you which one I'll use!".
Within the
foo<int*>you have said "this is going to specialize somefoofunction template, and the first argument will beint*.What more, the arguments will be
(int*).Let's look at our options!
For #1, we have
foo<int*>(int*). If we feedT=int*into it:yay! It works, possible case.
Now lets try the second:
oh oh, here we have
foo<int*>(int**). That doesn't match our specialization function signature. So we aren't specializing this.Thus, the compiler picks the first one as the only template function you could possibly be specialization.
...
Going back to your first attempt where you left it up to the compiler to do magic, it ends up seeing both as plausible, but the
foo(T*)is more specialized because anythingfoo(T*)could match, so couldfoo(T), but the other way around doesn't work. And a bunch of logic in the standard uses concepts like more specialized (or, rather, not less specialized) to order choices....
As a general rule, I'm going to advise you simply avoid function template specialization. Because it interacts with function template overloading in frankly unintuitive ways, it is usually better to dispatch to non-overlapping overloads before doing anything fancy like that.
Ie:
here, we create a
foo(int*)overload.Alternatively:
here we permit
static_cast<void(*)(int*)>(&foo<int>)to be a pointer to the int specialized implementation, but we never do any specialization of function templates.Fully understanding the rules of function template specialization is hard. You can have heuristics, and trust that it mostly works, but fully understanding it requires some serious study. So to avoid having to do that study for a skill that is honestly not that useful, just don't use function template specializations.
With overloading, especially with modern C++
requiresclauses andconstexpr if, you can do almost everything you want to do without using it.