我有以下代码:
class A {
public:
operator int() const { return 5; }
};
class B {
public:
operator int() const { return 6; }
};
int main() {
A a;
B b;
int myInt = true ? a : b;
return 0;
}
尝试使用Visual Studio 2017 RC编译该代码会导致以下错误:
错误 C2446 :
:
:无法从B
转换为A
注意:没有可用于执行此转换的用户定义转换运算符,或者无法调用运算符
...这是令人惊讶的,因为我希望它将它们转换为通用类型,在本例中为int
。
clang
(4.0)成功编译相同的代码,没有任何错误或警告。
在这种情况下哪两个是正确的,为什么?
答案 0 :(得分:14)
TL; DR; clang
是正确的,因为A
和B
之间没有可能的转换,因此使用重载决策来确定转换为应用于操作数,并选择以下(虚构)重载运算符:
int operator?:(bool, int, int);
对于任何算术类型对,?:
运算符都存在此类(再次,虚构)重载(请参阅下面的参考资料)。
由于您无法将A
转换为B
或B
转换为A
,因此以下情况适用:
否则,结果是prvalue。 如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv限定的)类类型,则使用重载决策来确定要应用于操作数([over.match.oper]的转换(如果有)), [over.built])。 如果重载决策失败,则程序格式错误。 否则,应用如此确定的转换,并使用转换后的操作数代替本子条款其余部分的原始操作数。
这可以追溯到这个:
如果任一操作数的类型是类或枚举,则可能是用户定义的操作符函数 声明实现此运算符或用户定义的转换可能是转换操作数所必需的 到适合内置运算符的类型。
[...]
重载决议的候选函数集是成员候选人,非成员候选人和内置候选人的联合。
如果通过重载决策选择了内置候选,则类类型的操作数将转换为所选操作函数的相应参数的类型,但user-defined conversion sequence的第二个标准转换序列是没有申请。 然后将运算符视为相应的内置运算符,并根据[expr.compound]进行解释。
在您的情况下,有一个内置候选人:
对于每对提升的算术类型
L
和R
,存在形式的候选运算符函数LR operator?:(bool, L, R);
其中
LR
是类型L
和R
之间通常的算术转换([expr.arith.conv])的结果。 [注意:与候选函数的所有这些描述一样,此声明仅用于描述内置运算符以实现重载解析。运算符“?:
”无法重载。 - 结束记录]
由于?:
运算符无法重载,这意味着只有两种类型都可以转换为算术类型(例如int
)时,代码才有效。作为“计数器”示例,以下代码格式不正确:
struct C { };
struct A { operator C() const; };
struct B { operator C() const; };
auto c = true ? A{} : B{}; // error: operands to ?: have different types 'A' and 'B'
另请注意,如果其中一种类型可转换为两种不同的算术类型,例如int
和float
,则会出现模糊的“调用”:
struct A { operator int() const; };
struct B {
operator int() const;
operator float() const;
};
auto c = true ? A{} : B{};
错误(来自gcc)实际上充满了信息:
错误:三元'运算符不匹配?:'(操作数类型为'bool','A'和'B')
auto c = true ? A{} : B{}; ~~~~~^~~~~~~~~~~
- 注意:候选人:
operator?:(bool, float, int) <built-in>
- 注意:候选人:
operator?:(bool, float, float) <built-in>