最佳可行的转换功能

时间:2019-06-11 14:30:19

标签: c++ language-lawyer implicit-conversion overload-resolution reference-binding

在以下程序中,应该选择哪个(如果有)转换功能,为什么?

int r;
struct B {};
struct D : B {};
struct S {
  D d;
  operator D&(){r=1; return d;} // #1
  operator B&(){r=2; return d;} // #2
};
int main() {
  S s;
  B& b = s;
  return r;
}

gcc和clang选择均选择转换功能#2。但是为什么呢?

标准says

  

(1)在[dcl.init.ref]中指定的条件下,可以将引用直接绑定到将转换函数应用于初始化程序表达式的结果。   重载解析用于选择要调用的转换函数。   假设“对cv1 T的引用”是要初始化的引用的类型,而“ cv S”是初始化程序表达式的类型,而S为类类型,则候选函数的选择如下:

     

(1.1)-考虑S的转换函数及其基类。那些未隐藏在S中且产生类型“对cv2 T2的左值引用”(初始化对函数的左值引用或右值引用时)或“ cv2 T2”或“对cv2 T2的右值引用”的非显式转换函数将右值引用或左值引用初始化为函数),其中“ cv1 T”与“ cv2 T2”的引用兼容)是候选函数。对于直接初始化,未隐藏在S中的那些显式转换函数会产生类型为“对cv2 T2的左值引用”(初始化函数的左值引用或右值引用时)或“对cv2 T2的右值引用”(初始化时为右值引用或对函数的左值引用)(其中T2与T的类型相同或可以通过资格转换转换为T型)也是候选函数。

     

(2)参数列表有一个参数,它是初始化程序表达式。   [注意:此参数将与转换函数的隐式对象参数进行比较。   —注   ]

在这里,我们有两个候选函数#1和#2。两者都可行-如果删除其中之一,程序仍会编译。 这两个转换函数仅采用隐式参数,并且具有相同的cv和ref限定。因此,没有一个应该是最可行的,并且该程序也不应编译。为什么会编译?

1 个答案:

答案 0 :(得分:5)

众所周知,重载解析分为三个阶段:(1)枚举候选函数; (2)确定哪些候选功能可行; (3)选择最佳可行功能。

根据[over.match.best] / 1:

  

...如果对于所有参数 i ,ICS i F1被定义为比另一个可行函数F2更好的函数。 em>(F1)的转换顺序并不比ICS i F1)差,然后

     
      
  • 对于某些自变量 j ,ICS j F1)比ICS j ({{1 }}),或者,否则,
  •   
  • 上下文是通过用户定义的转换进行初始化(请参见11.6、16.3.1.5和16.3.1.6)以及从F2的返回类型到目标类型(即,类型)的标准转换顺序从标准F1返回类型到目标类型[example ...]或(如果不是)
  • ,则是比标准转换顺序更好的转换顺序。   
  • [...进一步的决胜局规则...]
  •   

F2到#1或#2的隐式对象参数的隐式转换是身份转换,因此ICS1(#1)和ICS2(#1)不可区分,第二个要点是在这里相关。在#1的情况下,需要从派生到基数的转换才能从转换函数的返回类型即s转换为所需的类型即D&。在#2的情况下,标准转换顺序是身份转换(从B&B&),效果更好。因此,在这种情况下,选择功能#2优于#1。