87 Advanced The Hundred
The Specialization Nobody Called
you hand-wrote the int* case; now watch who answers
#include <iostream>
template <class T> void dump(T) { std::cout << "generic\n"; }
template <class T> void dump(T*) { std::cout << "pointer\n"; }
template <> void dump<int*>(int* p) { std::cout << "int pointer -> " << *p << "\n"; }
int main() {
int n = 42;
dump(n);
dump(&n);
}
Run it. What does it print?
Why¶
An explicit specialization is not an overload, and it does not take part in overload
resolution: the compiler first ranks the base templates, and only then asks the winner
whether it has a specialization for these arguments. For dump(&n) both templates match
perfectly — T = int* for the first, T = int for the second — and partial ordering
declares the T* version more specialized, so template #2 wins and prints pointer.
But template <> void dump<int*>(int*) specialized template #1: for #2, dump<int*>
would mean void dump(int**), which does not fit that parameter at all. So the
specialization is attached to the template that lost, and never gets a vote — it is
perfectly healthy, just never consulted, and dump<int*>(&n) still reaches it by name.
Moving the specialization above the T* overload changes nothing; writing it instead as
template <> void dump<int>(int*) does — that attaches to the winning template, and fires.
GCC 11 says nothing about any of this, even with -Wall -Wextra -pedantic.
The fix¶
Prefer a plain function overload — overloads are what resolution actually ranks, and an exact non-template match beats every template:
If you genuinely need the specialization machinery, specialize a class template and let one thin function forward to it — a class template has no overload set to lose in, so its specializations are matched against the arguments directly.
Takeaway: overload resolution chooses the template first; only the winner's specializations ever get a say.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo