Reputation: 33
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
Reputation: 1457
This seems compliant with the C++ standard.
From the C++17 draft standard, section 8.16:
- If either the second or the third operand has type
void
, [...]- Otherwise, if the second and third operand are glvalue bit-fields of the same value category, [...]
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 typeT1
to a target type related to the typeT2
of the operand expressionE2
as follows
- If
E2
is an lvalue, the target type is “lvalue reference toT2
”, subject to the constraint that in the conversion the reference must bind directly to an lvalue- If
E2
is an xvalue, [...]- 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
- If
T1
andT2
are the same class type (ignoring cv-qualification), [...]- 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.
- If the second and third operands are glvalues of the same value category and have the same type, [...]
- 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, [...]
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:
- The second and third operands have the same type; [...]
- 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
[...]
A a1;
auto c1 = b ? a1 : 0;
0
is an prvalue of type int
a1
is an lvalue of type A
Rule 4 applies:
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&
.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
.
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
.
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