不幸的是,这个问题的答案可能比您想要的更复杂。正如 Orbit 中的 Lightness Races 指出的那样,编译器确实会拒绝不明确的转换,但是这些转换是否不明确?让我们看几个案例。所有引用均参考 C++11 标准。
显式转换
这并没有直接解决您的问题,因为您询问了隐式转换,但由于轨道中的 Lightness Races 给出了显式转换的示例,因此我无论如何都会讨论它。
显式转换是从A
to B
when:
- 你使用语法
(B)a
, where a
属于类型A
,在这种情况下相当于static_cast<B>(a)
(C++11 标准,§5.4/4)。
- 您使用静态强制转换,在本例中将创建一个临时变量,该临时变量的初始化方式与声明的方式相同
B t(a);
初始化t
; (§5.2.9/4)
- 你使用语法
B(a)
,这相当于(B)a
因此也做了与声明中的初始化相同的事情B t(a);
(§5.2.3/1)
因此,在每种情况下,都会对类型的纯右值执行直接初始化B
使用类型值A
作为论点。 §8.5/16 规定仅考虑构造函数, so B::B(const A&)
将被调用。 (更详细的信息,请参阅我的回答:https://stackoverflow.com/a/22444974/481267 https://stackoverflow.com/a/22444974/481267)
复制初始化
在复制初始化中
B b = a;
价值a
类型的A
首先转换为临时类型B
使用用户定义的转换序列,这是一个隐式转换序列。然后这个临时用于直接初始化b
.
因为这是由不同类类型的对象对类类型进行复制初始化,both转换构造函数B::B(const A&)
和转换函数A::operator B()
是转换的候选者(§13.3.1.4)。之所以称其为后者,是因为它赢得过载决议。请注意,如果B::B
有争论A&
而不是const A&
,重载将不明确并且程序无法编译。有关标准的详细信息和参考,请参阅此答案:https://stackoverflow.com/a/1384044/481267 https://stackoverflow.com/a/1384044/481267
复制列表初始化
复制列表初始化
B b = {a};
只考虑构造函数B
(§8.5.4/3),而不是转换函数A
, so B::B(const A&)
将被调用,就像显式转换一样。
函数参数的隐式转换
如果我们有
void f(B b);
A a;
f(a);
那么编译器必须选择最佳的隐式转换序列进行转换a
输入B
为了将它传递给f
。为此,考虑用户定义的转换序列,其中包含一个标准转换,后跟一个用户定义的转换,再后跟另一个标准转换(§13.3.3.1.2/1)。用户定义的转换可以通过转换构造函数进行B::B(const A&)
或者通过转换函数A::operator B()
.
这就是事情变得棘手的地方。标准中有一些令人困惑的措辞:
由于隐式转换序列是初始化,因此初始化的特殊规则
按用户定义的转换 在为用户定义的转换选择最佳的用户定义的转换时适用
顺序(见 13.3.3 和 13.3.3.1)。
(§13.3.3.1.2/2)
长话短说,这意味着用户定义的转换序列中的用户定义的转换A
to B
本身受到重载解析的影响;A::operator B()
战胜B::B(const A&)
因为前者的简历资格较少(如在复制初始化的情况下),如果我们有的话,就会导致歧义B::B(A&)
而不是B::B(const A&)
。请注意,这不会导致重载解析的无限递归,因为不允许用户定义的转换将参数转换为用户定义的转换的参数类型。
退货声明
In
B foo() {
return A();
}
表达方式A()
隐式转换为类型B
(§6.6.3/2) 因此,应用与函数参数的隐式转换相同的规则;A::operator B()
将被调用,如果我们有的话,重载将是不明确的B::B(A&)
。然而,如果换做是
return {A()};
那么这将是一个复制列表初始化(再次§6.6.3/2);B::B(const A&)
将被调用。
Note:处理异常时不会尝试用户定义的转换; Acatch(B)
块不会处理throw A();
.