Reputation: 2967
I have the following definition.
using namespace std;
template <typename T>
void foo(const T &s) {
cout << 1;
}
template <typename T>
void foo(const T *s) {
cout << 2;
}
int main(int argc, const char * argv[]) {
char str[] = "ss";
char *s = str;
foo(s);
return 0;
}
Then it outputs
1
From my understanding, both versions have to go through a const conversion. Then void foo(const T *s)
is more specialized and should be invoked. However the compiler chose void foo(const T& s)
. What is the explanation?
Upvotes: 23
Views: 3547
Reputation:
The reason is because when the argument is a pointer, you have to pass in a pointer.. Like this:
// Example program
#include <iostream>
#include <string>
using std::cout;
using std::endl;
void foo(int *a)
{
cout << *a << endl;
}
int main()
{
int a = 5;
foo(&a);
}
But if your argument is a refrence you can just pass the parameter as it is like this:
// Example program
#include <iostream>
#include <string>
using std::cout;
using std::endl;
void foo(int &a)
{
cout << a << endl;
}
int main()
{
int a = 5;
foo(a);
}
Upvotes: 1
Reputation: 507373
Some people have pointed out that the parameters of the templates as chosen by the compiler
void f(char * const&)
void f(const char *);
Here, notice that the compiler expects a pointer to char, char*
, for the first function and it's a const reference. It may come as a surprise, if you found that for your case it prefers the first template, but for the following two, it will prefer the second
template <typename T>
void foo(const T& s) {
cout << 1;
}
template <typename T>
void foo(T &s) {
cout << 2;
}
So, of course, it will look at the const sometimes. Why didn't it in your case? Because it will only look at the const
for a reference if the other function has also a reference.
In your case, from char*
to const char*
it's a pointer conversion, but from lvalue
to const lvalue
, it's not actually a conversion. Adding a const by a const reference is ignored by overload resolution except when both functions have reference parameters as in the above case.
Upvotes: 15
Reputation: 173034
both versions have to go through a const conversion.
The 1st one don't need conversion. For template parameter const T&
with template argument char *
, T
will be deduced as char*
and then the type of function parameter will be char* const &
, so it's perfect match. The function argument will be bound to reference to const (T
-> const T&
, i.e. char*
to char* const&
), const qualified is not conversion.
For the 2nd one, the template parameter const T*
with template argument char *
, T
will be deduced as char
and then the type of function parameter will be const char*
, and qualification conversion is needed to convert char*
to const char*
.
From your comment
Both routines add low-level const to
s
.
The 1st one is adding const
to s
, but the 2nd one isn't. It's adding const
to what s
points to, not s
itself. This is the difference.
Upvotes: 2
Reputation: 14734
I have moved the const
without changing the semantics of the code, just so you are not surprised when the position of const
"changes" later.
template <typename T>
void foo(T const &s) {
cout << 1;
}
template <typename T>
void foo(T const *s) {
cout << 2;
}
char *x = "x";
foo(x);
Overload 1 will have to deduce T
to be char*
so the type of s
will be char * const &
(reference to const pointer to non-const char
). Such a reference can bind to the argument type (char *
; pointer to non-const char
) without any conversion.
Overload 2 will have to deduce T
to be char
so the type of s
will be char const *
(pointer to const char
). This incurs a qualification conversion from the argument type (char *
; pointer to non-const char
) to the parameter type (char const *
; pointer to const char
).
A more illustrative example of the principle in overload 1 is the following:
int n = 42;
int const &i = n;
Binding a const reference to a non-const entity doesn't involve a conversion, because it's the reference that adds the qualification, it's not the qualification being added to the entity in order to match it to the reference type.
Upvotes: 4
Reputation: 51
The reason is because s is a non-const pointer, so int* const& is actually a better match than int const* because it doesn't have to add const to the pointer type.
If s were const qualified then it would be an exact match for the T const* version.
Upvotes: 5