Reputation: 17193
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
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
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
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