What's confusing you is that your templates are not exactly replicating the behavior of the overloads. When the compiler is performing (non-template) overload resolution, it finds that const int*
is a better match than const int&
because there is no conversion necessary for int*
in the pointer overload (however, both have qualifier conversion, which is not as important).
However, for your templates things are a little different
This:
template<typename T>
void test_template(const T &ref){
std::cout << "By reference\n";
}
can be instantiatated as const int& ref
, yes, but it can also be instantiated as int* const& ref
(Because T
is const, and T
is int*
, and to make a const pointer, you place const
after int*
). Compare this to:
template<typename T>
void test_template(const T *ptr){
std::cout << "By pointer\n";
}
where, T
can be instantiated as int
to obtain const int*
So now the compiler has to ask itself, which is a better match,
int* const& ref
, or
const int* ptr
?
And the answer is the first one. Given that references and cv qualifiers are removed before ordering templates, we're left comparing
So the second one requires a type transformation from int*
to const int*
, whereas the first one doesn't. (Pointer syntax is weird, you have remember that in our case above, the const
that's left over is part of the type, not a qualifier). Note that if you had defined p
as const int *p = 0;
the compiler would have selected the second one.
To completely recreate the behavior of your non-template overloads, you'd have to explicitly test against T
being a pointer type:
template<typename T, typename std::enable_if<!std::is_pointer<T>::value, int>::type = 0>
void test_template(const T& ref){
std::cout << "By reference\n";
}
template<typename T>
void test_template(const T* ptr){
std::cout << "By pointer\n";
}
However, I think what's "good enough" would really be to just move const
to the other side of T*
for the pointer overload:
template<typename T>
void test_template(const T &ref){
std::cout << "By reference\n";
}
template<class T>
void test_template(T* const ptr){
std::cout << "By pointer\n";
}
This works because first cv qualifiers and references are removed from types when ordering templates. That leaves us with