Reputation: 195
This question is more theoretical and the scope is different from :
Template Specialization VS Function Overloading - Explaining that the compiler does overload resolution before it even looks at specializations
Template specialization vs. Function overloading - Describing the difference between the two mechanisms
Let's get into theoretical questions:
template <typename T>
T add(T a, T b)
{
return a + b;
}
template <>
int add<int>(int a, int b)
{
return a + b; //Specialization
}
int add(int a, int b)
{
return a + b; //Overloading
}
add(3,4); // in Main
1. When to use function full specialization and when to use function overloading?
Why to use template specialization since the templates are parsed twice (at template definition and at instantiation)?
On the other hand, in the case of function overloading, the answer seems pretty obvious: use it when you have a specific, particular case that needs a different logic rather than the template generic one.
2. Is the lookup process (for gcc or clang) going to choose every time the overloading candidate instead of the specialization if the form is the same? By form I mean: function name, number of arguments, argument types.
In the case of full function specialization, when the template functions are candidates, the compiler selects the template instances. Some are chosen, based on the accepted conversions (in order: strict exact match, qualification adjustment, inheritance derived to base conversion for virtual).
In the case of function overloading, among the candidate functions, select the viable ones for the call. Among the viable functions, select the best match for the call. Basically, the compiler checks the conversion strength (in order: strict exact match, qualification adjustment, int/float promotions, conversions, user conversions such as cast).
Normally, in case of ambiguity for the best viable between a template (specialization) and a non-template (overloading), the compiler selects the non-template. But why? How does the lookup mechanism work?
One factor might be the fact that the supported conversions are not the same. Example:
template <typename T>
bool isEqual(const T& a, const T& b); //generic form
template <>
bool isEqual(const string& a, const string& b); //specialization
bool isEqual(const string& a, const string& b); //overloading
bool c = isEqual ("cheers", "noroc"); //in Main, the arguments are const char *
In this case, the specialization does not match since it would require a user-defined conversion const char * -> string which is forbidden in argument deduction context. On the other hand, the overloading match since the user-defined conversion is valid here.
but what if in Main, we give strings as arguments?
string s1, s2;
bool c = isEqual (s1, s2);
Why does the compiler choose the overloading function in this case?
Upvotes: 2
Views: 285
Reputation: 39818
The most fundamental difference is that overloads are found independently by name lookup, whereas specializations are found through the original template itself. The result is that overloads that appear after the call are found only by ADL:
template<class T> void f(T) {} // #1
template<class T> void g(T t) {f(t);}
void f(int) {} // #2
template<> void f(char) {} // #3
namespace N {
struct A {};
void f(A) {} // #4
}
void h() {
f(1); // calls #2
g(1); // calls #1
g('1'); // calls #3
g(N::A()); // calls #4
}
While overload resolution prefers a function over a function template specialization with the same signature, only an explicit specialization completely prevents implicitly instantiating the primary template (which can be selected otherwise with f<>('a')
).
Another important (and the most famous) difference is that overloaded function templates behave very similarly to partial specializations (which are not available for function templates). This extends even to picking the best overload when more than one matches (via partial ordering). Of course, the limitation on name lookup pertains, so this isn’t a good means of expressing customizations that might appear after their usage; the idiomatic way to combine these features (and enable template argument deduction) is to have a “front man” function template that forwards calls to the appropriate specialization of a class template (which might be generated from a partial specialization).
Upvotes: 1