Incompatible operand types when using ternary cond

2019-04-03 23:56发布

This code:

  bool contains = std::find(indexes.begin(), indexes.end(), i) != indexes.end();
  CardAbility* cardAbility = contains ? new CardAbilityBurn(i) : new CardAbilityEmpty;

gives me the following error:

Incompatible operand types CardAbilityBurn and CardAbilityEmpty

However if I write the code like this:

 if (contains)
 {
    cardAbility = new CardAbilityBurn(i);
 }
 else
 {
    cardAbility = new CardAbilityEmpty;
 }

then the compiler doesn't mind. Why so? I want to use ternary conditional operator because it is just one line. What's wrong there?

I need to note (I think you might need this information) that CardAbilityEmpty and CardAbilityBurn both derive from CardAbility so they are so to say brothers.

Thanks

2条回答
Explosion°爆炸
2楼-- · 2019-04-04 00:12

There are several cases described for Microsoft compilers, how to handle operand types.

If both operands are of the same type, the result is of that type.

If both operands are of arithmetic or enumeration types, the usual arithmetic conversions (covered in Arithmetic Conversions) are performed to convert them to a common type.

If both operands are of pointer types or if one is a pointer type and the other is a constant expression that evaluates to 0, pointer conversions are performed to convert them to a common type.

If both operands are of reference types, reference conversions are performed to convert them to a common type.

If both operands are of type void, the common type is type void.

If both operands are of the same user-defined type, the common type is that type.

If the operands have different types and at least one of the operands has user-defined type then the language rules are used to determine the common type. (See warning below.)

And then there is a caution:

If the types of the second and third operands are not identical, then complex type conversion rules, as specified in the C++ Standard, are invoked. These conversions may lead to unexpected behavior including construction and destruction of temporary objects. For this reason, we strongly advise you to either (1) avoid using user-defined types as operands with the conditional operator or (2) if you do use user-defined types, then explicitly cast each operand to a common type.

Probably, this is the reason, Apple deactivated this implicit conversion in LLVM.

So, if/else seems to be more appropriate in your case.

查看更多
Evening l夕情丶
3楼-- · 2019-04-04 00:25

C++'s type system determines expressions' types from the inside out[1]. That means that the conditional expression's type is determined before the assignment to CardAbility* is made, and the compiler has to choose with only CardAbilityBurn* and CardAbilityEmpty*.

As C++ features multiple inheritance and some more possible conversion paths, since none of the types is a superclass of the other the compilation stops there.

To compile successfully, you need to provide the missing part : cast one or both operands to the base class type, so the conditional expression as a whole can take that type.

auto* cardAbility = contains
    ? static_cast<CardAbility*>(new CardAbilityBurn(i))
    : static_cast<CardAbility*>(new CardAbilityEmpty  );

(Note the use of auto, since you already provided the destination type in the right-side expression.)

It is however a bit convoluted, so in the end the if-else structure is better-suited in this case.

[1] There is one exception : overloaded function names have no definitive type until you convert them (implicitly or explicitly) to one of their versions.

查看更多
登录 后发表回答