Understanding Function Template Selection
In C++, a more specialized version of a class template is called a partial specialization. While the syntax for specialized function templates is the same as for partial specializations, the behavior can be somewhat different. Hence, the term "partial specialization" is not used with regard to functions. While class templates are partially specialized, functions are simply overloaded. The function template overload a compiler chooses in response to a particular call depends on the concept of which one is "more specialized" than the others. The process is similar to selecting one candidate over the other if and only if the set of all functions possibly generated from the selectee is a proper subset of another.
For example, consider these two function templates:
template<class T> void foo(T t); template<class T> void foo(std::vector<T> t);
Of these two, the second would be considered more specialized than the first. When considering the first function template, the compiler could generate a function with distinct types starting out like this:
void foo(int t); void foo(float t); void foo(std::string t); ...
For the second, the compiler could generate:
void foo(std::vector<int> t); void foo(std::vector<float> t); void foo(std::vector<std::string> t); ...
Both of these lists could go on forever; they are unbounded sets. In the first list, the parameter can be any distinct type, but in the second, the parameter must be the vector class specialized on a type. Any element in the second list could be used in the first, while the reverse is not true. This makes the second list a proper subset of the first.
Using function templates as described here is intuitive and useful. The subset discussion is a simplification, however, since the criterion for whether a function generated by one template function could be used by another is whether it is considered a match. A "match" means the function parameters arrived at by argument deduction are an exact match, with no implicit conversions. The process by which one function template is preferred over another is called partial ordering. Of course, there are additional rules and special cases for both partial ordering and argument deduction.
There are also areas where the writers of compilers, programs, and standards dont always agree. It is a good idea to stay away from these areas. For example, consider these two function templates:
template<class T> int foo(T t); template<class T> int foo(const T& t);
Given a call to these functions, such as the following:
MyClass mc; foo(mc);
some compilers will report that the call foo(mc) is ambiguous. Others will compile just fine and pick the second function for execution.
Here is another example:
template<class T> int foo(T*); template<class T> int foo(T&);
Given this call:
MyClass * mc = new MyClass(); foo(mc);
different compilers will report that foo(mc) is ambiguous, or compile but disagree with each other about which function actually gets called. Staying away from these fringe areas will make you a happier engineer. For a more precise description of the rules on how function templates are selected, see the C++ Standard under "Partial Ordering of Function Templates" (Chapter 14, Section 5.5.2) and "Template Argument Deduction" (Chapter 14, Section8.2).