Sir Nico
Sir Nico

Reputation: 33

Why does this conditional operator evaluate to int?

I stumbled across a strange (for me) behavior. Here is my code:

struct A 
{
  operator unsigned long long() const { return 1ull << 32; }
};

A a1;
unsigned long long a2 = 1ull << 32;

bool b = rand() % 2;
auto c1 = b ? a1 : 0;
auto c2 = b ? a2 : 0;

Why is c1 of type int and not unsigned long long like c2? And why is there no conversion warning generated (VC++)?

It took me a day to figure out what's wrong with my application.

Upvotes: 2

Views: 118

Answers (1)

Vincent Saulue-Laborde
Vincent Saulue-Laborde

Reputation: 1457

This seems compliant with the C++ standard.

C++17 standard

From the C++17 draft standard, section 8.16:

  1. If either the second or the third operand has type void, [...]
  2. Otherwise, if the second and third operand are glvalue bit-fields of the same value category, [...]
  3. Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, [...] an attempt is made to form an implicit conversion sequence (16.3.3.1) from each of those operands to the type of the other. [...] Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows

    1. If E2 is an lvalue, the target type is “lvalue reference to T2”, subject to the constraint that in the conversion the reference must bind directly to an lvalue
    2. If E2 is an xvalue, [...]
    3. If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has (possibly cv-qualified) class type
      1. If T1 and T2 are the same class type (ignoring cv-qualification), [...]
      2. otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue,array-to-pointer, and function-to-pointer standard conversions.

    Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand to the target type determined for the third operand, and vice versa. If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed. If no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below. Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section.

  4. If the second and third operands are glvalues of the same value category and have the same type, [...]
  5. Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, [...]
  6. Lvalue-to-rvalue (7.1), array-to-pointer (7.2), and function-to-pointer (7.3) standard conversions are performed on the second and third operands. After those conversions, one of the following shall hold:

    1. The second and third operands have the same type; [...]
    2. 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

    [...]

First case:

A a1;
auto c1 = b ? a1 : 0;
  • 0 is an prvalue of type int
  • a1 is an lvalue of type A

Rule 4 applies:

  • E1=0 to E2=a1: Attempt to apply rule 4.1. The target type is A&. However there is no implicit conversion from int to an lvalue A&.
  • E1=a1 to E2=0: rule 4.3.2 applies. The target type is int. There is an implicit conversion, using A::operator unsigned long long().

Therefore following rule 4, the return type of this conditional expression is int.

Second case:

unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
  • 0 is an prvalue of type int
  • a2 is an lvalue of type unsigned long long

Rule 7.2 applies: the return type of the conditional operator is determined by the arithmetic conversions rules of the two expressions. Here it is unsigned long long.

About compiler warnings

Both clang (demo) and g++ (demo) will raise a warning with the option -Wconversion. I've no experience of MSVC, but it also seems to have warnings for dangerous arithmetic conversions: C4242, C4365. Check that you have them enabled.

Upvotes: 1

Related Questions