Aviv Cohn
Aviv Cohn

Reputation: 17193

Why can't I use the ternary operator here?

This line won't compile:

Shape shape = (i % 2) ? Circle(5) : Rectangle(5, 5);

(I know it's useless since whatever the expression returns will be reduced to a simple Shape, that's not the point).

Can't figure out why it won't compile. I'm creating a Shape variable named shape (which I think at this point creates a new Shape), and then I'm assigning this variable the result of an expression. Why doesn't this compile?

The error:

no match for ternary operator

What's really weird is that the longer code with exact same meaning does compile and run as expected:

Shape shape;
if (i % 2)
     shape = Rectangle(5, 5);
else shape = Circle(5);

Upvotes: 7

Views: 5310

Answers (3)

T.C.
T.C.

Reputation: 137315

The detailed conversion rules for the conditional operator are rather complex (you can find the full quote from the standard in this answer if you are interested). The short of it is that, when used with objects of class type, it will attempt to convert its second operand to match the type of the third, and its third operand to match the type of the second, but it won't try to convert both to a third class type.

Since Circle isn't convertible to Rectangle and Rectangle isn't convertible to Circle, the compiler will complain (well, unless the two types define some odd conversion to pointer, scoped enumeration or arithmetic types, in which case §5.16 [expr.cond]/p5 comes into play).

Note also that your assignment will slice the object, which probably isn't a good idea.

Upvotes: 13

Dietmar Kühl
Dietmar Kühl

Reputation: 153840

When type types of the two branches in the conditional operator differ, one needs to be convertible to the other according to 5.16 [expr.cond] paragraph 16:

Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. ...

The other cases referred to don't apply: they are about the types being void or one branch being a throw expression. The omission simply explains how the conversions are attempted and in which cases the conversion is chosen (essentially, if the type of one expression uniquely converts to the other but not the other way around).

Assuming your Circle or your Rectangle are convertible to Shape you can explicitly convert one or both expressions to Shape.

Upvotes: 3

Kerrek SB
Kerrek SB

Reputation: 477040

The second and third operand of the conditional operator have to have a common type, as can be determined with the std::common_type trait. Interestingly perhaps, two classes derived from a common base class do not have that base class as a common type, nor are pointers or references thus related. Further thought quickly shows that indeed such a notion doesn't make sense: two classes can have any number of base classes in common, and there is no way in general to select a unique, preferred base.

If you want to use derived classes in the conditional operator, you have to cast the types yourself, explicitly.

A more realistic and sensible example of your code would be something like this:

Shape const & s = ask_user() ? static_cast<Shape const &>(show_me_a_circle())
                             : static_cast<Shape const &>(squares_all_the_way());

std::cout << "Your shape is " << s.get_colour() << ".\n";

Upvotes: 7

Related Questions