This is an example from C++ Primer, 4th edition, Chapter 16 and it's about template specialization.
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
template <class T>
int compare(const T& v1, const T& v2) {
if(v1<v2) return -1;
if(v2<v1) return 1;
return 0;
}
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1,v2);
}
int main(int argc, const char *argv[])
{
cout << compare("abc","defg") << endl;
return 0;
}
I expect compare("abc","defg")
will call the specialized version of the template.
But the fact is, g++ 4.6.3 won't compile this code and give the follow error:
error: no matching function for call to 'compare(const char [4], const char [5])'
note: candidate is: template int compare(const T&, const T&)
Now given the following facts:
I. string literals, or C-style string in C++ is actually a const char array.
II. If passed as plain, non-reference types, an array will be converted to a pointer to its first element quietly.
Here I just pass string literals "abc" and "defg" as reference to const char*
, and I expect they will be converted to const char*
first and then passed by reference.
But it seems that g++ disagree with me and refuse to compile the code.
But if I replace template specialization with function overloading, that is, replace
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1,v2);
}
with
int compare(const char* const& v1, const char* const& v2){
return strcmp(v1,v2);
}
then g++ will be happy to compile it.
So where on earth does the problem lie? Why I cannot pass string literals by parameter type const char* const&
in the template specialization version ?
The answer below is based on explanation from C++ Templates: The complete guide pp57: Using String literals as Arguments for Function templates
.
template <class T>
int compare(const T& v1, const T& v2) {
if(v1<v2) return -1;
if(v2<v1) return 1;
return 0;
}
This requires that both parameters v1
and v2
have the same type.
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1,v2);
}
This requires that you have parameters with const char *
type.
However, "abc"
has type char const[4]
whereas "defg"
has type char const[5]
. They are different types. Since both the specialized and templated version required reference parameters, there is no array-to-pointer decay during argument deduction. Therefore, you cannot pass different length string literals to both of them to find a match. If you provide a regular function, which does not require any argument deduction, the compiler will find a match.
If you declare non-reference parameters, you can substitute them with string literals of different length. The reason for this behavior is that during argument deduction array-to-pointer
conversion (often called decay) occurs only if the paramter does not have a reference type.
Template specializations do not participate in overload resolution process. Only the primary template is considered by overload resolution.
Template specializations come into play only later and only if their primary template "wins" overload resolution. I.e. template specializations are used in the process of specialization (as the name suggests), they are completely invisible during overload resolution.
For this reason, in your first example, you have only one candidate considered by overload resolution
template <class T> int compare(const T& v1, const T& v2);
In order to succeed, this candidate should pass through template argument deduction for your set of arguments. (Template argument deduction process does not care about any additional specializations either.) Template argument deduction fails in this case, since for argument of array type template parameter T
is deduced as an array. And you get incompatible deductions for two arguments. The compiler gave you the error message that describes the problem. In other words, in your first example the specialized version of the template never has a chance to come into play.
In your second example, where you replaced specialization with overloading, you provided a second candidate for overload resolution. Now the compiler sees both
template <class T> int compare(const T& v1, const T& v2);
int compare(const char* const& v1, const char* const& v2);
The template candidate fails just like it did before, while the overloaded candidate succeeds.
To better illustrate the how template specializations work in this case, we can take your original code and change the primary template in order to help it to pass through overload resolution by decoupling the parameters from each other. If in your first example you change the template declaration to
template <class T1, class T2>
int compare(const T1& v1, const T2& v2) {
...
leaving everything else unchanged, the code will compile and it will use your specialization. But even in that case the primary template with deduced parameters will be seen as a better match to your arguments (immediate reference binding with no conversions).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With