I have this working code:
template <typename X, typename Y>
auto f(X a, Y b) { return a b; };
template <typename X, typename Y>
auto f(X* a, Y* b) { return *a *b; };
int main() {
int* p;
int* q;
f(p, q);
}
which compiles and runs the second f()
, because it is more specialized.
However, if we const
-qualify the arguments like so:
template <typename X, typename Y>
auto f(const X a, const Y b) { return a b; };
template <typename X, typename Y>
auto f(const X* a, const Y* b) { return *a *b; };
int main() {
int* p;
int* q;
f(p, q);
}
It no longer works and tries to pick the first function, erroring out on the a b
for pointes.
Why does const
-qualifying the types make the second template no longer more specialized?
CodePudding user response:
Why does
const
-qualifying the types make the second template no longer more specialized?
It doesn't.
The "problem" we're observing here is that overload resolution takes into account a few things before checking what type is more specialized.
Reading the section Best viable function we can see that:
For each pair of viable function F1 and F2, the implicit conversion sequences from the
i
-th argument toi
-th parameter are ranked to determine which one is better (except the first argument, the implicit object argument for static member functions has no effect on the ranking)F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2 [...]
Now let's take a look at both overloads:
template <typename X, typename Y>
auto f(const X a, const Y b) { return a b; };
int main() {
int* p;
int* q;
f(p, q);
}
Here X = int*
and Y = int*
. No conversions are necessary. It's worth to point out that the types of a
and b
are not const int*
, but int* const
.
Let's now take a look at the second overload:
template <typename X, typename Y>
auto f(const X* a, const Y* b) { return *a *b; };
int main() {
int* p;
int* q;
f(p, q);
}
Here, in order to get const int*
(the desired type of the argument) from int*
(which is the type of p
and q
), X
and Y
have to be deduced as int*
and then a conversion has to occur. That's because const int*
doesn't mean const
pointer to int
, but a pointer to const int
. Pointer to const int
and pointer to int
are not the same, but the latter is convertible to the former. There is no way to deduce X
and Y
for it to be no conversions in this case.
Thus, overload resolution chooses the candidate with fewer conversions. That overload tries to add two pointers and it results in a compile-time error.
CodePudding user response:
Template-1: expects const int
template <typename X, typename Y>
auto f(const X a, const Y b) { return a b; };
Template-2: expects pointer to const int
template <typename X, typename Y>
auto f(const X* a, const Y* b) { return *a *b; };
Updated driver function:
int main() {
const int i = 10;
int const * p = &i;
f(p, p); // instantiates Template-2
f(i, i); // instantiates Template-1
}
In orignal code error occurs as compiler is trying to instantiates Template-2 with pointer to non-const integer.
Working sample: https://cppinsights.io/s/7db84985