2

在下面的程序中,应该选择哪个(如果有)转换函数,为什么?

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 select 都选择转换函数#2。但为什么?

标准

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

(1.1) - 考虑 S 及其基类的转换函数。那些未隐藏在 S 中并产生类型“对 cv2 T2 的左值引用”(初始化对函数的左值引用或右值引用时)或“cv2 T2”或“对 cv2 T2 的右值引用”(当初始化对函数的右值引用或左值引用),其中“cv1 T”与“cv2 T2”引用兼容,是候选函数。对于直接初始化,那些不隐藏在 S 中并产生类型“对 cv2 T2 的左值引用”(当初始化对函数的左值引用或右值引用时)或“对 cv2 T2 的右值引用”(当初始化一个右值引用或函数的左值引用),

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

这里我们有两个候选函数#1 和#2。两者都是可行的——如果其中一个被删除,程序仍然可以编译。两个转换函数都只采用隐式参数,并且具有相同的 cv- 和 ref-qualification。所以没有一个应该是最好的,程序不应该编译。为什么会编译?

4

1 回答 1

5

好吧,如您所知,重载解决分为三个阶段:(1)枚举候选函数;(2) 确定哪些候选功能是可行的;(3)选择最佳可行函数。

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

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

  • 对于某些参数j,ICS j ( F1) 是比 ICS j ( F2) 更好的转换序列,或者,如果不是,
  • 上下文是通过用户定义的转换(参见 11.6、16.3.1.5 和 16.3.1.6)F1进行的初始化,并且从比从返回类型到目标类型 [example ...] 的标准转换序列更好的转换序列,F2或者,如果不是,
  • [ ... 进一步的决胜局规则 ... ]

从 #1 或 #2 的隐式对象参数所需的隐式转换s是身份转换,因此 ICS1(#1) 和 ICS2(#1) 无法区分,第二个要点与此处相关。在#1 的情况下,需要进行派生到基类的转换,以将转换函数的返回类型(即D&)转换为所需的类型,即B&。在 #2 的情况下,标准转换顺序是恒等转换 ( B&to B&),这样更好。因此,在这种情况下,函数#2 被选为优于#1。

于 2019-06-11T15:03:17.777 回答