为什么添加目标类型转换运算符仍无法避免C++初始化时的二义性问题?
为什么添加目标类型转换运算符仍无法避免C++初始化时的二义性问题?
咱们先把你遇到的情况拆解开来唠,先把你的代码贴出来,方便咱们揪出问题根源:
class A { public: A(int i) {} A(bool b) {} }; class B { public: operator int() { return 1; }; operator bool() { return false; }; operator A() { return A(1); }; // 你以为加了这个就能彻底消除歧义 }; struct S { A a; }; int main() { B b; A a{b}; // 这个能正常跑 S s1{.a = b}; // 这个也没问题 // 报错的大概率是类似 S s{b}; 这种初始化写法 }
首先得搞明白:C++里不同的初始化场景,编译器的处理逻辑完全不一样,这就是你踩坑的核心!
你看A a{b}和S s1{.a = b}这俩能正常工作,是因为它们都是直接针对A类型的初始化:
- 写
A a{b}的时候,编译器一眼就看到你要造个A对象,这时候你加的operator A()是最直接的路径——直接把B转成A,一步到位。而另两条路(B转int再调用A的int构造、B转bool再调用A的bool构造)需要先转类型再构造,编译器会觉得这两条路绕了弯,而且按照规则,这种“两次转换”的路径优先级更低,所以直接选了最直接的那条,自然没歧义。
那为啥有的写法还是会报错呢?就拿S s{b}这种聚合初始化来说:
S是个聚合类型(简单说就是没有自定义构造函数的结构体/类),当你写S s{b}时,编译器要做的是用b去初始化成员a。这时候编译器的“思考逻辑”变了——它会把所有能把b转成A的路径都列出来,而且会觉得两条路径的“成本”完全一样:
- 路径一:直接用
operator A()把B转成A(一次类型转换) - 路径二:用
operator int()把B转成int,再调用A的int构造函数造个A对象(也是一次类型转换,后面调用构造函数不算在转换成本里)
按照C++的重载决议规则,这两条路径都只包含一次用户定义的类型转换,优先级是完全相同的。编译器没法判断哪条路更好,就只能摊手报歧义错误了——这可不是你加的转换运算符没用,而是编译器在这个场景下的“判定标准”和直接初始化A的时候不一样。
那怎么解决这个问题呢?给你俩简单的办法:
- 把B里的
operator int()和operator bool()改成explicit的,这样它们就不会被隐式调用,编译器就只能选直接转A的路径了; - 或者给A的构造函数加上
explicit,这样就算B转成了int,也没法隐式调用A的构造函数造A对象,自然也不会有歧义。
内容来源于stack exchange
相关产品推荐
相关产品推荐

