【问题标题】:How to compare two standard conversion sequences use the rank of contained conversions如何比较两个标准转换序列使用包含转换的等级
【发布时间】:2021-03-22 15:58:23
【问题描述】:
#include <iostream>
void g(int*);  //#1
void g(int (&arr)[2]);  //#2

void f(int*);  //#3
void f(int const*);  //#4
int main(){
  int arr[2] ={0};
  f(arr);    // choose #3
  g(arr);  //ambiguous
}

考虑上面的代码,#3 被选择为f(ptr),但是,g(arr) 给出了ambiguous 诊断。

选择最佳函数的规则定义为:

标准转换序列S1是比标准转换序列S2更好的转换序列如果

  • S1 是 S2 的适当子序列(比较 [over.ics.scs] 定义的规范形式的转换序列,不包括任何左值变换;身份转换序列被认为是任何非- 身份转换序列)或者,如果不是这样

所以看看over.ics.scs#3

这些用于对标准转换序列进行排名。转换序列的排名是通过考虑序列中每个转换的排名和任何参考结合的排名来确定的。

根据我对上述规则的理解,我可以理解为什么#3f(ptr)的最佳重载,即:

给定 S1 为 (arr => int*):

Array-to-pointer conversion -> (identity conversion)  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^                   
     int[2] => int*             int* => int* 

当给定 S2 为 (ptr => int const*)

Array-to-pointer conversion -> Qualification conversions ->  identity conversion   
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^ 
     int[2] => int*               int* => int const*           int const* => int const* 

由于identity conversionQualification conversions 的正确子序列,因此 S1 优于 S2。因此,f(ptr) 的重载决议选择了 #3

当我使用类似的过程来确定最适合g(arr) 时,我遇到了一个问题。

同样,给定 S1 为 (arr => int*)

Array-to-pointer conversion -> identity conversion  
^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^ 
      int[2] => int*              int* => int*

当给定 S2 as(arr => int (&arr)[2])

当引用类型的参数直接绑定到参数表达式时,隐式转换序列是恒等转换,除非参数表达式的类型是参数类型的派生类,在这种情况下,隐式转换序列是派生到基础的转换

identity conversion
^^^^^^^^^^^^^^^^^^^
  bind to reference   

这里,S2identity conversionS1Array-to-pointer conversion 的正确子序列,因此它应该比S1 更好,为什么编译器抱怨g(arr) 是一个模棱两可的调用?

我对如何对标准转换序列进行排名有任何误读吗?如何比较两个标准 ICS(包含转换的排名)?

【问题讨论】:

  • 目标示例使用函数模板,但相同的 ics 排名规则适用于实例化的函数。
  • @cigien 该示例不解释这个问题。尽管它们都有精确的排名,但是身份转换比任何其他具有精确排名的转换都要好。否则,为什么f(ptr) 是明确的,qualification conversion 没有确切的排名吗?
  • 澄清一下,你问为什么调用g(arr); 是模棱两可的,对吗?或者您是否正在寻找专门针对您对规则的理解的回复?
  • @cigien 两者。在我看来,f(arr);g(arr); 中的每次转换都只包含exact rank 的转换,但为什么g(arr); 是模棱两可的。我需要这些问题的解释。
  • 啊,好的。为什么f(arr) 不模棱两可可能有一个目标,但我还是会重新提出这个问题。

标签: c++ language-lawyer


【解决方案1】:

重点在这里:

S1是S2的一个真子序列(比较[over.ics.scs]定义的规范形式的转换序列,排除任何左值变换;恒等转换序列被认为是一个任何非身份转换序列的子序列),或者,如果不是那个

这意味着,对于函数调用g(arr),所有数组到指针的转换都不会用于确定排名。换句话说,从int[2] 类型到int* 类型,只有一个用于确定排名的身份转换。因此,void g(int*); 的 S1 和 void g(int (&amp;arr)[2]); 的 S2 是无法区分的 ICS,因此编译器给出了一个模棱两可的错误。

相比之下,用于比较排名的void f(int*);void f(int const*); 的转化分别为identity conversionqualification conversion

根据规则:

身份转换序列被认为是任何非身份转换序列的子序列

因此,Qualification conversion 的排名被认为比identity conversion 的排名更差。所以,void f(int*) 赢得了比赛。

【讨论】:

  • 否,资质转换,属于资质调整类别,与身份排名相同。他们都被评为完全匹配
【解决方案2】:

您正在尝试将over.ics.rank-3.2.1 应用于这些重载集,但此规则不适用于fg


给定调用f(arr);,在为f 执行重载解析时,两个重载都需要一个由数组到指针 转换组成的标准转换序列,并且两者具有相同的等级,这是完全匹配。本例中使用的决胜局是over.match.best#over.ics.rank-3.2.5

标准转换序列S1是比标准转换序列S2更好的转换序列如果

...

S1 和 S2 的区别仅在于它们的资格转换 ([conv.qual]) 并分别产生相似的类型 T1 和 T2,其中 T1 可以通过资格转换转换为 T2。

此规则后面有一个示例,演示了该规则的工作原理。

对于重载集fT1int *T2int const *T1 可以通过限定转换转换为T2


在调用g(arr); 的情况下,当执行重载解析时,重载g(int (&amp;)[2]) 被列为完全匹配,因为所需的标准转换顺序是No Conversion必需

不过,重载g(int*) 也被列为完全匹配,因为所需的标准转换顺序是数组到指针的转换。

但是,与 f 不同的是,[over.ics.rank] 中没有规则可以消除 g 的标准转换序列之间的歧义,因此调用失败。

【讨论】:

  • 你能告诉我“S1是一个适当的子序列”适用于什么情况吗?
  • @jackX 可能,但我得考虑一下。无论如何,这将是一个单独的问题,我真的不想在 cmets 中讨论。
  • 回到这个问题,“S1 和 S2 仅在它们的资格转换上有所不同”,但是,S1 不包含 qualification conversion 而 S2 包含 qualification conversion。引用的规则似乎说S1S2 都包含不同的qualification conversions。
  • this case,qualification conversion(消除身份转换是否为资格转换的含糊不清)绝对不能赋予本案特权权力(函数指针转换有完全匹配的排名)不再。这是身份转换函数指针转换。 IMO,over.ics.rank#3.2.1 在这里申请。
  • "引用的规则似乎是说 S1 和 S2 都包含不同的资格转换。" 不是真的,只是可以将一个转换为另一个。另外,我不确定您的演示是否适用:据我所知,不允许将 noexcept 说明符添加为资格转换
猜你喜欢
  • 2016-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-18
相关资源
最近更新 更多