Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Result of ternary operator on `int` and `float`

In short, I have the following code:

float x = cond ? 0 : x_option;

Where x_option is a template<float>, which has an operator float() (and no other automatic cast operators. Note the types of the expression after this conversion:

bool ? int : float;

I would expect the result of this expression to be a float:


C11:

If both the second and third operands have arithmetic type, the result type that would be determined by the usual arithmetic conversions, were they applied to those two operands, is the type of the result.

So it follows the same rules as for example float + int.


C++11:

The second and third operands have arithmetic or enumeration type; the usual arithmetic conversions are performed to bring them to a common type, and the result is of that type.

Same rule.


However, both clang and cl.exe generate a vcvttss2si instruction in the path leading to the evaluation of the 3rd argument, i.e. they decide that the result of bool ? int : float is an int, not a float!

To be fair, C++11 has some rules that should apply before the one I quoted, and to be honest, the rules are not the easiest to understand, although from what I can make of it, conversion to int shouldn't be possible:

C++11:

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 process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

— If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (Clause 4) to the type “lvalue reference to T2”, subject to the constraint that in the conversion the reference must bind directly (8.5.3) to an lvalue.

— If E2 is an xvalue: E1 can be converted to match E2 if E1 can be implicitly converted to the type “rvalue reference to T2”, subject to the constraint that the reference must bind directly.

— If E2 is an rvalue or if neither of the conversions above can be done and at least one of the operands has (possibly cv-qualified) class type:

  • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to a prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand.

  • Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E2 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to a prvalue (or the type it has, if E2 is a prvalue).

Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, or one can be converted but the conversion is ambiguous, the program is ill-formed.

From what I understand in the above, this falls into the second case of the third case (last bullet point of the last bullet point), so E1 (of type int) should be converted to the type of E2 (float). The standard later says if the conversion can be done both ways, the program is ill-formed.

What's the ruling here? Am I in the realm of undefined behavior? Or is the standard dictating the conversion I am seeing?

like image 909
Shahbaz Avatar asked Mar 09 '18 19:03

Shahbaz


1 Answers

bool ? int : float is a float.

bool ? int : some_type_convertible_to_float isn't: the third operand can be converted to int (by converting first to float then to int); the int (probably) can't be converted to some_type_convertible_to_float. So by the rules you cited, you get an int.

like image 63
T.C. Avatar answered Sep 22 '22 00:09

T.C.