3
votes

Consider this example

struct A { };
template<class T> struct B {
  template<class R> int operator*(R&);              // #1
};

template<class T, class R> int operator*(T&, R&);   // #2

The partial ordering will apply to #1 and #2 to select the best viable function template.

Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph)

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. [ Note: The type replacing the placeholder in the type of the value synthesized for a non-type template parameter is also a unique synthesized type.  — end note ] If only one of the function templates M is a non-static member of some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”.

So, the original type for #2 is int operator*(T&, R&) and its transformed type is int operator*(UniqueA&, UniqueB&), there's no doubt to the original type of #2. However, I don't know what's the original type for #1(member function template).

The structure of that rule seems that the emphasized part in the above rule should be considered as a step of producing the transformed template.
So, whether the original type of #1 is int operator*(B<T>&, R&) or int operator*(R&). If it's the latter, that wouldn't consistent with common sense. Since int operator*(R&) and int operator*(T&, R&) do not match the number of parameters, how to compare them (A against P)?

How to read the rule for producing the transformed template correctly? If the emphasized part is not considered as a step of the transformation, instead it's a general rule for member function during the partial ordering, Does the rule make it misleading to place such a rule after the process of the transformation?

1
"origin type" isn't a term that appears in the Standard; what do you mean by this?ecatmur
@ecatmur I mean the original type for #1 during the partial ordering. "Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type."xmh0511

1 Answers

1
votes

Yes, it's a bit of a mess. As you've observed, it doesn't make sense for the "original type" of a class non-static member function to lack the inserted this parameter.

The only way to make it work is for the subsequent paragraphs in subclause 3 to apply to the "original" function type on the other side of [temp.deduct.partial] as well as the "transformed" function type. You could just about read it that way for what is presently the first sentence of the second paragraph, reading "each" as applying to both the transformed and original function type:

Each function template M that is a member function is considered to have a new first parameter of type X(M) [...]

However, since the resolution to CWG 2445 in P2108 we have another sentence:

If exactly one of the function templates was considered [...] via a rewritten candidate with a reversed order of parameters, then the order of the function parameters in its transformed template is reversed.

So we quite clearly have this reversal applying asymmetrically, giving an absurd result. On the upside, it's fairly clear how it should read; the adjustments to function type (this insertion and parameter reversal) should apply prior to unique type synthesis/substitution, and apply to the "original" and transformed function type equally.

As far as I can tell, this defect does not appear to have been reported to the Core Language Working Group; it does not appear in the C++ Standard Core Language Active Issues list.