使用三元运算符进行奇怪的隐式转换

时间:2017-01-13 14:45:07

标签: c++ type-conversion operator-keyword implicit-conversion ternary

我有以下代码:

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)成功编译相同的代码,没有任何错误或警告。

在这种情况下哪两个是正确的,为什么?

1 个答案:

答案 0 :(得分:14)

TL; DR; clang是正确的,因为AB之间没有可能的转换,因此使用重载决策来确定转换为应用于操作数,并选择以下(虚构)重载运算符:

int operator?:(bool, int, int);

对于任何算术类型对,?:运算符都存在此类(再次,虚构)重载(请参阅下面的参考资料)。

标准规则:

由于您无法将A转换为BB转换为A,因此以下情况适用:

  

[expr.cond]

     

否则,结果是prvalue。   如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv限定的)类类型,则使用重载决策来确定要应用于操作数([over.match.oper]的转换(如果有)), [over.built])。   如果重载决策失败,则程序格式错误。   否则,应用如此确定的转换,并使用转换后的操作数代替本子条款其余部分的原始操作数。

这可以追溯到这个:

  

[over.match.oper]

     

如果任一操作数的类型是类或枚举,则可能是用户定义的操作符函数   声明实现此运算符或用户定义的转换可能是转换操作数所必需的   到适合内置运算符的类型。

     

[...]

     

重载决议的候选函数集是成员候选人,非成员候选人和内置候选人的联合。

     

如果通过重载决策选择了内置候选,则类类型的操作数将转换为所选操作函数的相应参数的类型,但user-defined conversion sequence的第二个标准转换序列是没有申请。   然后将运算符视为相应的内置运算符,并根据[expr.compound]进行解释。

在您的情况下,有一个内置候选人:

  

[over.built#27]

     

对于每对提升的算术类型LR,存在形式的候选运算符函数

LR      operator?:(bool, L, R);
     

其中LR是类型LR之间通常的算术转换([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'

另请注意,如果其中一种类型可转换为两种不同的算术类型,例如intfloat,则会出现模糊的“调用”:

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>
  •