1
votes

Thanks to partial ordering of function templates, the following seems to be unambiguous:

template <class T> void f(T*) {
   cout << "T*" << endl;
}

template <class T> void f(const T*) {
   cout << "const T*" << endl;
}

int main(){
    int *p = nullptr;
    f(p);
}

T is deduced as int in both cases, and both specializations are viable, with the second requring an implicit qualification conversion from int * to const int*. Both are added to the overload set.

To find the best viable function, we turn to partial ordering. We synthesize new types for each and get void f(X*) and void f(const Y*). Then we do type deduction between the two. [temp.deduct.partial]¶8:

Using the resulting types P and A, the deduction is then done as described in 17.8.2.5. (...) If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

Does deduction succeed for template <class T> void f(T*) given const Y*? Yes, with T=const Y.

Does deduction succeed for template <class T> void f(const T*) given X*? Yes, with T=X (requiring an implicit qualification conversion).

So X and Y are both at least as specialized as the other, which means that both specializations are at least as specialized as the other, which means that the call is ambiguous.

Except that it isn't, so where did I go wrong above?

2
I'm pretty sure this has nothing to do with templates. If you remove them you get the same thing: coliru.stacked-crooked.com/a/9bfac6487f40393c. Adding const is not an exact match, so the exact match is preferred.NathanOliver
@NathanOliver overload resolution rules are different for function templates and regular functions. I'm asking because I want to understand what the standard is actually saying about function template overload resolution.knatten
i dont get the second part of your answer where you mention "Does deduction succeed for template <class T> void f(T*) given const Y*", in your example p is a int* not a const int*463035818_is_not_a_number
@user463035818 yes, that's because during partial ordering the argument template is the synthesized type, not the type at the call site.knatten

2 Answers

1
votes

I've figured out where I went wrong:

To find the best viable function, we turn to partial ordering.

No, we don't. To find the best viable function, we go through the steps in [over.match.best] in order. Partial ordering is just one point on that list, which we in fact never get to because the best viable function is found by a previous step on the list (for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)).

The two specializations are indeed equally specialized, but it doesn't matter since we never use partial ordering in the first place.

0
votes

template <class T> void f(const T*) is indeed more specialized than template <class T> void f(T*) but the later is an exact match.

If you look at overload_resolution#best_viable_function:

Best viable function:

1) there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
[..]
5) or, if not that, F1 and F2 are both template specializations and F1 is more specialized according to the partial ordering rules for template specializations