Template Specialization VS Function Overloading
Other than differences in how overload resolution proceeds, a template specialization locks you into a signature. Meaning you can't do this:
template <>
long add<int>(int a, int b)
// ^- the return type is long , not int
{
return a + b; //no reason to specialize, but still...
}
A compiler will complain about the changed return type, since the declaration (and function signature) is determined by the primary template. A compiler doesn't even consider the specialization when doing overload resolution. It just instantiates a declaration from the primary, and that declaration must match what it ends up calling.
So overloads are more flexible in how you can define them. And there is no ambiguity for the compiler. The overload resolution mechanism favors a non-template over a template generated function if their argument types match. More often than not it's easier to overload than to specialize a function template.
It makes no difference in this case, but there are cases where it will. Lets look at this sample code
template<class T> // (a)
void f(T);
template<> // (b)
void f<>(int*);
template<class T> // (c)
void f(T*);
int main()
{
int *p;
f(p);
}
When we call f(p);
, what function would you expect to be called? Most people would go with b
, and they would be wrong. The reason for this is because specializations do not apply to overload resolution. They are a special recipe for a template that tells the compiler that if you deduce this type, then stamp out the template this way instead.
So what the compiler does is go through overload resolution and says I can call a
with T
being int*
, or I can call c
with T
being int
. Since the latter is more specialized (T
is narrower), c
wins. This can be very surprising but it makes a lot of sense once you ignore the specialization and just consider the overloads.
Walter E. Brown has a very in depth video about templates and specialization called “C++ Function Templates: How Do They Really Work?” that you should consider watching. It is an hour long, but it is very detailed.
In "simple" cases, they would behave identically, but they are some cases where they differ.
A trivial difference, is the explicit call add<int>
which will call the specialization and not the overload.
Other differences happens for overload resolution, a call like:
add(42, 4.2f);
will fail with the template specialization (int
and float
, so T
cannot be deduced)
whereas, overload is valid and will convert the float
to int
.
Fist variant is simpler because compiler will have to collect and then choose from set of candidates containing only a single item add<int>(int, int)
specialization. While second variant will cause compiler to perform some extra job by collecting a set of candidates containing two items - add<int>(int, int)
and add(int, int)
and choose between them using fancy function overload ranking algorithm.