Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial ordering on T*... and const T&

cppreference claims the following

template <class ...T> int f(T*...);  // #1
template <class T>  int f(const T&); // #2
f((int*)0); // OK: selects #1
            // (was ambiguous before DR1395 because deduction failed in both directions)

If we follow DR1395 we see

If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails. Otherwise, using Using the resulting types P and A, the deduction is then done as described in 17.9.2.5 [temp.deduct.type]. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. 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.

[...]

If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing paramter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.

From what I can infer, this means we should be matching each individual type expanded from T*... to const T& and vice versa. In this case, T* is more specialized than const T& (T from U* succeeds, T* from U fails).

However, the compilers disagree. Clang thinks it's ambiguous and gcc thinks the second should be called, both of which differs from cppreference.

What is the correct behaviour?

like image 925
Passer By Avatar asked Mar 17 '19 19:03

Passer By


1 Answers

cppreference is correct for this example (which is exactly the example from the CWG issue, as well as CWG 1825). Let's go through the deduction both ways.


Deduce template <class ...T> int f(T*...); from const U&. This fails, not going to be able to deduce T* from const U& - the fact that it's a pack here is immaterial. So #2 is not at least as specialized as #1.


Deduce template <class T> int f(const T&); from U*... We used to have the rule "If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails." - which would have meant that we failed before we even tried to do anything else. But CWG 1395 removed that sentence, so we keep going, and we have the new sentence:

Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template.

We compare U*, by itself, to each remaining parameter type, which is const T&. That deduction succeeds. So #1 is at least as specialized as #2.


As a result, now #1 is more specialized than #2. The quote you cite about trailing parameter packs as a later tiebreaker doesn't apply - since we don't have the case where each function template is at least as specialized as the other. Also the other quote you cite ([temp.deduct.type]/10 is about deducing function types, so I don't think it applies here either? Although I'm also not sure about the example in that section - or what that particular rule actually means.

like image 117
Barry Avatar answered Oct 12 '22 16:10

Barry