三元运算符的奇怪隐式转换
我有以下代码:
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 编译该代码会导致以下错误:
Attempting to compile that code with Visual Studio 2017 RC results in the following error:
错误 C2446: :
: 没有从 B
到 A
error C2446:
:
: no conversion fromB
toA
注意:没有可以执行此转换的用户定义转换运算符,或者无法调用该运算符
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
...这很令人惊讶,因为我希望它将它们都转换为通用类型,在本例中为 int
.
...which is surprising because I would expect it to convert both of them to a common type, in this case int
.
clang
(4.0) 成功编译相同的代码,没有任何错误或警告.
clang
(4.0) compiles the same code successfully without any errors or warnings.
在这种情况下,两者中哪一个是正确的,为什么?
Which of the two is correct in this case, and why?
推荐答案
TL;DR; clang
是对的,因为 A 之间没有可能的转换
和 B
,重载决议用于确定要应用于操作数的转换,并选择以下(虚构的)重载运算符:
TL;DR; clang
is right, since there are no possible conversions between A
and B
, overload resolution is used to determine the conversions to be applied to the operands, and the following (fictive) overloaded operator is selected:
int operator?:(bool, int, int);
对于任何一对算术类型,?:
运算符都存在这种(同样是虚构的)重载(参见下面的参考资料).
There exists such (again, fictive) overload of the ?:
operator for any pair of arithmetic types (see references below).
由于您无法将 A
转换为 B
或 B
转换为 A
,因此以下适用:
Since you cannot convert A
to B
or B
to A
, then the following applies:
[expr.cond]
否则,结果为纯右值.如果第二个和第三个操作数的类型不同,并且都具有(可能是 cv 限定的)类类型,则使用重载决议来确定要应用于操作数的转换(如果有)([over.match.oper], [over.built]).如果重载决议失败,则程序格式错误.否则,将应用如此确定的转换,并使用转换后的操作数代替本小节其余部分的原始操作数.
Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands ([over.match.oper], [over.built]). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this subclause.
这又回到了这个:
[over.match.oper]
如果任一操作数的类型是类或枚举,则用户定义的运算符函数可能是声明实现此运算符或 用户定义的转换可能是转换操作数所必需的到适合内置运算符的类型.
If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator.
[...]
用于重载决策的候选函数集是成员候选者、非成员候选者和内置候选者的并集.
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates.
如果通过重载决议选择了内置候选,则将类类型的操作数转换为所选操作函数的相应参数的类型,除了 用户定义的转换顺序 不适用.然后算子被当作对应的内置算子,按照[expr.compound].
If a built-in candidate is selected by overload resolution, the operands of class type are converted to the types of the corresponding parameters of the selected operation function, except that the second standard conversion sequence of a user-defined conversion sequence is not applied. Then the operator is treated as the corresponding built-in operator and interpreted according to [expr.compound].
在你的情况下,有一个内置的候选人:
In your case, there is a built-in candidate:
[over.built#27]
对于每一对提升的算术类型 L
和 R
,都存在以下形式的候选运算符函数
For every pair of promoted arithmetic types L
and R
, there exist candidate operator functions of the form
LR operator?:(bool, L, R);
其中 LR
是通常算术转换的结果 ([expr.arith.conv]) 在类型 L
和 R
之间.[ 注意: 与所有这些候选函数的描述一样,此声明仅用于描述内置运算符以用于重载决策.运算符?:
"不能重载.—结束说明 ]
where LR
is the result of the usual arithmetic conversions ([expr.arith.conv]) between types L
and R
.
[?Note: As with all these descriptions of candidate functions, this declaration serves only to describe the built-in operator for purposes of overload resolution. The operator "?:
" cannot be overloaded. —?end note ]
<小时>
额外细节:
由于 ?:
运算符不能重载,这意味着您的代码只有在两种类型都可以转换为算术类型(例如,int
)时才有效.作为反"示例,以下代码格式错误:
Extra details:
Since the ?:
operator cannot be overloaded, this means that your code only works if both types can be converted to an arithmetic type (e.g., int
). As a "counter"-example, the following code is ill-formed:
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
,您会得到一个模棱两可的调用":
Also note that you would get an ambiguous "call" if one of the type is convertible to two different arithmetic types, e.g., int
and float
:
struct A { operator int() const; };
struct B {
operator int() const;
operator float() const;
};
auto c = true ? A{} : B{};
错误(来自gcc)实际上充满了信息:
The error (from gcc) is actually full of information:
错误:不匹配三元运算符?:"(操作数类型为布尔"、A"和B")
error: no match for ternary 'operator?:' (operand types are 'bool', 'A', and 'B')
auto c = true ? A{} : B{};
~~~~~^~~~~~~~~~~
- 注意:候选:
operator?:(bool, float, int) <built-in>
- 注意:候选:
operator?:(bool, float, float) <内置>
相关文章