Title
Partial ordering of function templates with unordered parameter pairs
Status
Section
13.10.3.5 [temp.deduct.partial]
Submitter
Mike Miller

Created on 2009-05-01.00:00:00 last changed 153 months ago

#### Messages

Date: 2009-10-15.00:00:00

Rationale (October, 2009):

The consensus of the CWG is that this issue, in which corresponding parameters cannot be compared but the functions are equivalent in terms of overload resolution, arises so infrequently in practice that no change is warranted at this time.

Date: 2022-02-18.07:47:23

Consider the following example:

```    template <typename T> void f(T** p, void (*)());  // #1
template <typename T> void f(T* p, void (&)());   // #2
void x();
void g(int** p) {
f(p, x);    // #3
}
```

The question is whether the call at #3 is ambiguous or not.

The synthesized declarations for overload resolution are:

```    void f<int>(int**, void(*)());   // From #1
void f<int*>(int**, void(&)());  // From #2
```

Neither of these is a better match on the basis of conversion sequences (the function-to-pointer conversion and the reference binding have “exact match” rank), and both are function template specializations, so the tiebreaker in 12.2.4 [over.match.best] paragraph 1 comes down to whether #1 is more specialized than #2 or vice versa.

The determination of whether one of these templates is more specialized than the other is done (as described in 13.7.7.3 [temp.func.order]) by synthesizing a type for the template parameter of each function template (call them @1 and @2, respectively), substituting that synthesized type for each occurrence of the template parameter in the function type of the template, and then performing deduction on each pair of corresponding function parameters as described in 13.10.3.5 [temp.deduct.partial].

For the first function parameter, #1 is more specialized: deduction succeeds with P=T* and A=@1**, giving T=@1*, but it fails with P=T** and A=@2*. For the second parameter, deduction fails in both directions, with P=void(*)() and A=void() as well as with P=void() and A=void(*)() (the reference is dropped from both the parameter and argument types, as described in 13.10.3.5 [temp.deduct.partial] paragraph 5). This means that neither parameter type is at least as specialized as the other (paragraph 8).

According to 13.10.3.5 [temp.deduct.partial] paragraph 10,

If for each type being considered a given template is at least as specialized for all types and more specialized for some set of types and the other template is not more specialized for any types or is not at least as specialized for any types, then the given template is more specialized than the other template. Otherwise, neither template is more specialized than the other.

According to this rule, #1 is not more specialized than #2 because it is not “at least as specialized” for the second parameter type, so the call at #3 is ambiguous.

Results vary among implementations, with some rejecting the call as ambiguous and others resolving it to #1.

Would it be better to say that a function template F1 is more specialized than F2 if at least one of F1's types is more specialized than the corresponding F2 type and none of F2's types is more specialized than the corresponding F1 type? That would be simpler and, for examples like this, arguably more intuitive. The rationale for this change would be that if, for a given parameter pair, neither is more specialized than the other, then that parameter pair simply says nothing about whether one of the templates is more specialized than the other, rather than indicating that the templates cannot be ordered.