5
template <typename T>
void show(T&);       // #1
template <typename T>
void show(T const&); // #2

int main()
{
    int a = 0;
    show(a);        // #1 to be called
}

I'm confused about these partial ordering rules. Here are some quotes: [temp.deduct.partial]/5

Before the partial ordering is done, certain transformations are performed on the types used for partial ordering:

  • If P is a reference type, P is replaced by the type referred to.

  • If A is a reference type, A is replaced by the type referred to.

[temp.deduct.partial]/6

If both P and A were reference types (before being replaced with the type referred to above), determine which of the two types (if any) is more cv-qualified than the other; otherwise the types are considered to be equally cv-qualified for partial ordering purposes. The result of this determination will be used below.

[temp.deduct.partial]/7

Remove any top-level cv-qualifiers:

  • If P is a cv-qualified type, P is replaced by the cv-unqualified version of P.

  • If A is a cv-qualified type, A is replaced by the cv-unqualified version of A.

Firstly, both void show(T&) and void show(T const&) could be called by passing an int lvalue, so we need to use partial order rules to decide which function is more matched. Then, according to the above quotes, we do some transformations. Step 1:

T&       => T          #3
T const& => T const    #4

Step 2:

T       => T    #5
T const => T    #6

#5 => #6, #6 => #5, deduction succeeds in both directions. Then the following rules work: [temp.deduct.partial]/9

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):

  • if the type from the argument template was an lvalue reference and the type from the parameter template was not, the parameter type is not considered to be at least as specialized as the argument type;

  • otherwise, if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the parameter type is not considered to be at least as specialized as the argument type.

So #4 is more specialized then #3. For a given value a, #2 function should be called, but actually #1 function is called. Why? Is there something wrong with my understanding?

L. F.
  • 19,445
  • 8
  • 48
  • 82
xmh0511
  • 7,010
  • 1
  • 9
  • 36
  • I am curious about this answer also, is it because a `const&` is not what you declared and passed? So it takes just the value as a reference. If you used `const int = 0` I think the second show function, the `const&` would be called. – ABC Dec 12 '19 at 02:51
  • @Raymond If you comment out any of these methods,the remained function also matched ,so They should be accroding to partical ordering rules to decide which is more matched,but the partical ordering rules select #2 ,the complier select #1,maybe there's a supplement to these partical ordering rules to make the complier to use #1,We need someone who knows to tell us – xmh0511 Dec 12 '19 at 03:11
  • Hi jack X. I have edited your question to include links to relevant parts of the standard. This helps other people gather context. Please do the same in your future questions. Thank you! – L. F. Dec 12 '19 at 09:42

1 Answers1

5

In this case, the "more specialized" rule is not used, which is a tiebreaker in case ranking implicit conversion sequences does not determine the order of the functions: [over.match.best]/1

Define ICSi(F) as follows:

[...]

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • (1.3) for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

  • [...]

  • (1.7) F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in [temp.func.order], or, if not that,

  • [...]

In this case, ranking implicit conversion sequences alone is enough to determine the ordering, so everything following the "or, if not that" is ignored. Deduction results in (int&) for #1 and (int const&) for #2, so in both cases, reference binding (a to int& and a to int const&) results in the Identity Conversion, regardless of the cv-qualification: [over.ics.ref]/1

When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion ([over.best.ics]). [...]

However, ICS1(#1) is a better conversion sequence than ICS1(#2) because of [over.ics.rank]/3:

Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:

  • [...]

  • (3.2) Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

    • [...]

    • (3.2.6) S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

  • [...]

Therefore, ICS1(F1) is a better conversion sequence than ICS1(F2), thus F1 is better than F2 according to [over.match.best]/(1.3) (above). The [over.match.best]/(1.7) rule is not used.


The "more specialized" rule is used in the reverse situation:

int const a = 0;
show(a);         // #2 should be called for a const lvalue

This time, deduction results in int const& and int const&, so [over.match.best]/(1.7) kicks in. The result, as you observed, is that #2 is a better function than #1.


(Emphasis mine, for all quotes)

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • I probably understand, but there are some doubts about "and the types to which the references refer are the same type except for top-level cv-qualifiers",how to understand this?for example,"T const value"and "U value",if T is the same as U and then the conditon is true or ,"T value" and"U value",only T is the same as U and both of them does not contain "const",the condition is true? which is the correct understanding? – xmh0511 Dec 12 '19 at 13:47
  • @jackX “same type except for top level cv qualifiers” means “same type, after the const and volatile are removed”. For example, for `const int` and `int` the condition is true, for `int` and `int` also true, but for `int` and `double` or `int` and `int*` it’s false. – L. F. Dec 12 '19 at 13:51
  • thanks "and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers" and this mean,if the arugment is "T value",the parament of F1 is "U&" and the parament of F2 is "U const& ",If T does not contain cv,then F1 is more matched,if T does contain cv ,then F2 is more matched than F1? – xmh0511 Dec 12 '19 at 13:57
  • @jackX Exactly. These two conclusions result from different rules, though, as indicated in the answer :) – L. F. Dec 12 '19 at 14:04
  • L.F. you are too modest :) – xmh0511 Dec 12 '19 at 14:36