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

jac*_*k X 4 c++ partial-ordering language-lawyer function-templates template-argument-deduction

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
}
Run Code Online (Sandbox Code Playgroud)

我对这些偏序规则感到困惑。以下是一些引用:[temp.deduct.partial]/5

在完成偏序之前,对用于偏序的类型执行某些转换:

  • 如果P是引用类型,P则替换为引用的类型。

  • 如果A是引用类型,A则替换为引用的类型。

[temp.deduct.partial]/6

如果PA都是引用类型(在被上面提到的类型替换之前),确定这两种类型中的哪一种(如果有的话)比另一个更符合 cv 限定;否则,出于偏序目的,这些类型被视为同样具有 cv 限定。下面将使用该确定的结果。

[temp.deduct.partial]/7

删除任何顶级 cv 限定符:

  • 如果P是 cv 限定类型,P则替换为 的 cv 非限定版本P

  • 如果A是 cv 限定类型,A则替换为 的 cv 非限定版本A

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

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
}
Run Code Online (Sandbox Code Playgroud)

第2步:

T&       => T          #3
T const& => T const    #4
Run Code Online (Sandbox Code Playgroud)

#5 => #6, #6 => #5, 两个方向的推导都成功。然后以下规则起作用:[temp.deduct.partial]/9

如果,对于给定类型,扣除在两个方向成功(即,类型是上述变换后相同的)和二者PA分别引用类型(被替换为类型提及的上述前):

  • 如果参数模板中的类型是左值引用而参数模板中的类型不是,则认为参数类型至少不像参数类型那样特殊;

  • 否则,如果参数模板中的类型比参数模板中的类型更具有 cv 限定(如上所述),则认为参数类型至少不会像参数类型那样专门化。

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

L. *_* F. 5

在这种情况下,不使用“更专业”的规则,这是一个决胜局,以防对隐式转换序列进行排名并不能确定函数的顺序:[over.match.best]/1

ICS i (F) 定义如下:

[...]

鉴于这些定义,如果对于所有参数i,ICS i (F1) 不是比 ICS i (F2)更差的转换序列,那么一个可行函数 F1 被定义为比另一个可行函数 F2 更好的函数 ,然后

  • ( 1.3 ) 对于某些参数j,ICS j (F1) 是比 ICS j (F2)更好的转换序列,或者,如果不是,

  • [...]

  • ( 1.7 ) F1 和 F2 是函数模板特化,根据 [temp.func.order] 中描述的偏序规则,F1 的函数模板比​​ F2 的模板更特化,或者,如果不是,

  • [...]

在这种情况下,仅对隐式转换序列进行排名就足以确定顺序,因此“或,如果不是”之后的所有内容都将被忽略。推导导致(int&)#1 和(int const&)#2,因此在这两种情况下,引用绑定(atoint&ato int const&)都会导致身份转换,而不管 cv 限定:[over.ics.ref]/1

当引用类型的参数直接绑定到参数表达式时,隐式转换序列是恒等转换,除非参数表达式的类型是参数类型的派生类,在这种情况下,隐式转换序列是派生的 -到基础转换([over.best.ics])。[...]

但是,由于[over.ics.rank]/3,ICS1(#1) 是比 ICS1(#2) 更好的转换序列:

两个相同形式的隐式转换序列是不可区分的转换序列,除非以下规则之一适用:

  • [...]

  • ( 3.2 ) 标准转换序列 S1 是比标准转换序列 S2 更好的转换序列,如果

    • [...]

    • ( 3.2.6 ) S1 和 S2 是引用绑定([dcl.init.ref]),除了顶级 cv 限定符之外,引用所引用的类型是相同的类型,引用初始化的类型by S2 引用比 S1 初始化的引用引用的类型更符合 cv 限定

  • [...]

因此,ICS1(F1) 是比 ICS1(F2) 更好的转换序列,因此根据[over.match.best]/(1.3)(以上),F1 优于 F2 。的[over.match.best] /(1.7)规则不被使用。


“更专业”的规则用于相反的情况:

int const a = 0;
show(a);         // #2 should be called for a const lvalue
Run Code Online (Sandbox Code Playgroud)

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


(强调我的,所有报价)