Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incompatible operand types when using ternary conditional operator

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

like image 401
Andrey Chernukha Avatar asked Apr 24 '15 08:04

Andrey Chernukha


People also ask

What are the 3 operands in ternary operator?

The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark ( ? ), then an expression to execute if the condition is truthy followed by a colon ( : ), and finally the expression to execute if the condition is falsy.

How many operands to ternary operators require?

The conditional operator (? :) is a ternary operator (it takes three operands).

What are the three arguments of a ternary operator?

The ternary operator take three arguments: The first is a comparison argument. The second is the result upon a true comparison. The third is the result upon a false comparison.

Can ternary operator have multiple conditions?

We can nest ternary operators to test multiple conditions.


2 Answers

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.

like image 192
Quentin Avatar answered Oct 19 '22 12:10

Quentin


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.

like image 2
Valentin H Avatar answered Oct 19 '22 11:10

Valentin H