请考虑以下代码段:
class A;
class B {
public:
B(){}
B(A&) // conversion constructor that takes cv-unqualified A
{
cout << "called B's conversion constructor" << endl;
}
};
class A {
public:
operator B() const // conversion operator that takes cv-qualified A
{
cout << "called A's conversion operator" << endl;
return B();
}
};
int main()
{
B b = A(); // who gets called here?
return 0;
}
根据this question,具有最少cv限定形式的转换序列获胜(规范中为13.3.3.2/3
):
标准转换序列S1 是一个更好的转换序列 标准转换序列S2,如果S1和S2是参考 绑定(8.5.3),以及引用引用的类型 除了顶级cv限定符之外的相同类型,以及 的类型 由S2引用初始化的引用比更符合cv 由S1初始化的引用引用的类型。
然而在上面的代码片段中,转换运算符总是被选中,无论A在两个函数中是否具有cv限定。唯一的例外是,当构造函数和运算符都是cv限定的时候,编译器会抱怨选择转换序列时不明确,而cv-unqualified case都没有(为什么?)。
所以问题是:
答案 0 :(得分:1)
出于重载解析的目的,A::operator B()
有一个隐式对象参数,其类型为cv A&
。该参数是特殊的,即使它是根据[over.match.funcs]/5的非const类型的左值引用,它也可以绑定到右值:
在重载解析期间,隐含的对象参数与其他参数无法区分。但是,隐式对象参数保留其标识,因为不能应用用户定义的转换来实现与它的类型匹配。对于未使用ref-qualifier声明的非静态成员函数,将应用其他规则:
- 即使隐式对象参数不是const限定的,也可以将rvalue绑定到参数,只要在所有其他方面,参数可以转换为隐式对象参数的类型。 [注意:这样的参数是rvalue的事实不会影响隐式转换序列的排名。 - 尾注]
除了隐式对象参数,参见[over.match.funcs],如果需要将除非对非易失性const类型的引用之外的左值引用绑定到右值,则无法形成标准转换序列或将右值引用绑定到函数左值以外的左值。 [注意:这意味着,例如,如果候选函数具有非const左值引用参数(隐式对象参数除外),则候选函数不能成为可行函数,并且相应的参数将需要创建临时函数来初始化左值参考(参见[dcl.init.ref])。 - 结束说明]
因此,如果转换构造函数采用非const参数,则转换运算符始终可行时不可行,这使得重载决策始终选择转换运算符。
如果转换构造函数采用const参数且转换运算符是非const,则两个隐式转换都是[over.ics.ref]/1的标识转换:
当引用类型的参数直接绑定到参数表达式时,隐式转换序列是标识转换...
然后根据[over.ics.rank]/3:
标准转换序列S1是比标准转换序列S2更好的转换序列,如果
...
S1和S2是引用绑定,引用引用的类型除顶级cv限定符外属于同一类型,S2引用初始化引用的类型更符合cv比S1引用的引用所引用的类型。
因此选择了非const版本(转换运算符)。
最后,如果两者都是const,它们之间没有区别,也没有特殊规则适用,因此重载决议因模糊而失败。