如何理解T&和T const&的偏序规则

How to understand the rules of partial ordering about T& and T const&

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
}

我对这些部分排序规则感到困惑。以下是一些引述:[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.

首先,void show(T&)void show(T const&)都可以通过传递int左值来调用,所以我们需要使用偏序规则来决定哪个函数更匹配。然后,根据上面的引述,我们做一些改造。第 1 步:

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

第 2 步:

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

#5 => #6,#6 => #5,双向推演成功。 然后以下规则起作用:[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.

所以 #4#3 更专业。对于给定的值 a,应该调用 #2 函数,但实际上调用了 #1 函数。为什么?我的理解有问题吗?

在这种情况下,不使用 "more specialized" 规则,这是一个决胜局,以防排名隐式转换序列不能确定函数的顺序:[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,

  • [...]

在这种情况下,仅对隐式转换序列进行排名就足以确定排序,因此 "or, if not that" 之后的所有内容都将被忽略。 #1 的推导结果为 (int&),#2 的推导结果为 (int const&),因此在这两种情况下,引用绑定(aint&aint const&) 导致身份转换,无论 cv 资格如何:[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]). [...]

但是,ICS1(#1) 是比 ICS1(#2) 更好的转换序列,因为 [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.

  • [...]

因此,ICS1(F1) 是比 ICS1(F2) 更好的转换顺序,因此根据 [over.match.best]/(1.3) (above). The [over.match.best]/(1.7) 未使用的规则,F1 优于 F2。


"more specialized"规则用于相反的情况:

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

这一次,推导结果为 int const&int const&,因此 [over.match.best]/(1.7) 开始。正如您所观察到的,结果是 #2 是一个比 #1 更好的函数.


(强调我的,所有引号)