在以下程序中,应该选择哪个(如果有)转换功能,为什么?
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限定。因此,没有一个应该是最可行的,并且该程序也不应编译。为什么会编译?
答案 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。