重载分辨率和部分模板排序

Bar*_*rry 19 c++ templates language-lawyer overload-resolution

考虑这对简单的函数模板.

template <typename T>
void foo(T& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

template <typename C>
void foo(const C& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
Run Code Online (Sandbox Code Playgroud)

如果我们foo使用非const参数调用:

int i = 4;
foo(i);
Run Code Online (Sandbox Code Playgroud)

T&过载是基于[over.ics.rank]优选/3.2.6,由于推定的参考int&是以下CV -qualified比推导的参考const int&.

但是,如果我们foo使用const参数调用:

const int ci = 42;
foo(ci);
Run Code Online (Sandbox Code Playgroud)

const C&过载是优选的,因为它是"更专门的"基于[over.match.best] /1.7.但是确定这个的规则是什么?我的理解是你合成了一个类型C(调用它M)并尝试执行演绎foo(M)- 但这会成功(带T == M).它只是一个rvalue会导致演绎失败 - 但编译器如何知道它必须在合成步骤中选择一个rvalue?

Col*_*mbo 20

免责声明:我们考虑的类型始终是参数类型.类型/值类别/等.传递的实际参数仅在重载决策中考虑,从不在部分排序中考虑.

部分排序考虑两个"转弯"中的两个重载,其中一个模板始终是参数模板,另一个模板是参数模板.[temp.deduct.partial]/2:

对于涉及的每个模板,都有原始函数类型和转换后的函数类型.[..]演绎过程使用变换后的类型作为参数模板,将另一个模板的原始类型用作参数模板.对于部分排序比较中涉及的每种类型,此过程完成两次:一次使用转换的模板-1作为参数模板,使用template-2作为参数模板,再次使用转换的模板-2作为参数模板和模板-1作为参数模板.

您应该熟悉生成转换的"模板"的方式.这在§14.5.6.2/ 3中规定.

要生成转换模板,对于每种类型,非类型或模板模板参数(包括其模板参数包(14.5.3))分别合成唯一类型,值或类模板,并将其替换为该参数的每次出现在模板的函数类型中.

所以我们的(转换的)参数模板是

void foo( Unique1& );

void foo( Unique2 const& );
Run Code Online (Sandbox Code Playgroud)

[temp.deduct.partial]/3&/ 4:

用于确定排序的类型取决于完成部分排序的上下文:

  • 在函数调用的上下文中,使用的类型是函数调用具有参数的函数参数类型.[..]

从参数模板和从参数模板中的相应类型的上述指定每种类型的被用作类型PA.

因此我们有两个转弯,并且我们都有一个类型P和类型A:

关闭1: ::  
    P1   T const&
    A1Unique1&

关闭2: ::  
    P2   T&
    A2Unique2 const&

但是在乐趣开始之前,对这些类型也进行了一些转换 - 我缩写为[temp.deduct.partial]/5&/ 7:

  • 如果P或是A引用,那么它们将被它们引用的类型替换.
  • 删除任何顶级cv限定符.

请注意,删除的cv限定符将在以后"记住".[temp.deduct.partial]/6:

如果同时PA分别引用类型,确定两个类型(如果有的话)更CV-合格比其他(与上面提到的类型被替换之前); 否则,对于部分订购目的,类型被认为是同等的cv资格.下面将使用该确定的结果.

因此,我们离开了

关闭1: ::  
    P1   T
    A1Unique1

关闭2: ::  
    P2   T
    A2Unique2

现在我们进行演绎 - 通过设置显然在两个转弯中都取得了成功T=Unique[1/2].来自[temp.deduct.partial]/8:

如果对于给定类型的推导成功,则参数模板中的类型被认为至少与参数模板中的类型一样专用.

这给了我们两者Unique1&至少同样具有专业性T const&,Unique2 const&至少同样具有专业性T&.


但是,这是[temp.deduct.partial] /(9.2)介入的地方:

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

  • [..]; 除此以外,

  • 如果参数模板中的类型比参数模板中的类型更具cv限定(如上所述),则参数类型不应被视为至少与参数类型一样专用.

记得的cv资格赛开始发挥作用.A2"更cv修饰(如上所述)"P2,因此P2不被认为是至少为专门为A2.

最后,[temp.deduct.partial]/10:

函数模板F是至少为专门为函数模板G 如果,对于每个对用于确定定购的类型,从类型F被至少为专门从类型G.
F比更专业G,如果F是至少作为专业GG不至少作为专业F.

意味着,因为类型T&专门的不是至少因为Unique2 const&我们已经建立的是T const&至少为专门为Unique1&中,T const&-过载比更加专业化T&-过载.


第9段中的上述规则目前是由R. Smith在四个月前创建的CWG#2088的主题:

适用于14.8.2.4 [temp.deduct.partial]第9段中左值与右值参考和cv资格的后期破坏者

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

但是,这是基于错误的假设.[..]我们需要决定规则是"两个方向上的演绎成功"还是"类型相同."后者似乎更合理.

这不会改变已建立的结果,因为我们得到的类型确实相同.