Why can't I use the ternary operator here?
Asked Answered
B

3

7

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);
Bridgettebridgewater answered 3/10, 2014 at 23:56 Comment(4)
Fyi, you're about to slice the hell out of your return object.Outdated
And.. your second example, assuming Circle and Rectangle both derived from Shape, definitely slices. Avoid both slicing and solve your polymorphic ternary indiscretion by using something else such as smart pointers. (see it live).Outdated
“I'm assigning this variable the result of an expression.” – There is the problem: The expression has no valid type.Frady
Possible duplicate of: #24706980Dhow
C
13

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.

Corned answered 4/10, 2014 at 0:4 Comment(5)
So, in Java ternary expression (if you can call it that) are simply another syntax for Thing variable; if (something) variable = stuff; else variable = otherStuff; So that's not the case in C++?Bridgettebridgewater
@KerrekSB Not really relevant to the question at hand. Pointers use the "composite pointer type" rules.Corned
@AvivCohn Java neither has multiple inheritance nor copy semantics. C++ is much more complicated.Frady
@T.C.: Yes, sure, I just wanted to point out that there are other ways you can get a third type that is neither of the original types, other than having class-type operands.Listlessness
@AvivCohn: In Java, you're dealing with references to objects, not objects.Undersized
L
7

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";
Listlessness answered 4/10, 2014 at 0:3 Comment(1)
This is sort of a tautology...common_type is defined using the conditional operator.Corned
P
3

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.

Pyrimidine answered 4/10, 2014 at 0:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.