Why does this conditional operator evaluate to int? - c++

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.

This seems compliant with the C++ standard.
C++17 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 type T1 to a target type related to the type T2 of the operand expression E2 as follows
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
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 and T2 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
[...]
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.

Related

why the C++ expression ( true/false ? value1 : value2 ) does not work in type-conversion [duplicate]

Why does the first return a reference?
int x = 1;
int y = 2;
(x > y ? x : y) = 100;
While the second does not?
int x = 1;
long y = 2;
(x > y ? x : y) = 100;
Actually, the second did not compile at all - "not lvalue left of assignment".
Expressions don't have return types, they have a type and - as it's known in the latest C++ standard - a value category.
A conditional expression can be an lvalue or an rvalue. This is its value category. (This is somewhat of a simplification, in C++11 we have lvalues, xvalues and prvalues.)
In very broad and simple terms, an lvalue refers to an object in memory and an rvalue is just a value that may not necessarily be attached to an object in memory.
An assignment expression assigns a value to an object so the thing being assigned to must be an lvalue.
For a conditional expression (?:) to be an lvalue (again, in broad and simple terms), the second and third operands must be lvalues of the same type. This is because the type and value category of a conditional expression is determined at compile time and must be appropriate whether or not the condition is true. If one of the operands must be converted to a different type to match the other then the conditional expression cannot be an lvalue as the result of this conversion would not be an lvalue.
ISO/IEC 14882:2011 references:
3.10 [basic.lval] Lvalues and rvalues (about value categories)
5.15 [expr.cond] Conditional operator (rules for what type and value category a conditional expression has)
5.17 [expr.ass] Assignment and compound assignment operators (requirement that the l.h.s. of an assignment must be a modifiable lvalue)
The type of the ternary ?: expression is the common type of its second and third argument. If both types are the same, you get a reference back. If they are convertable to each other, one gets chosen and the other gets converted (promoted in this case). Since you can't return an lvalue reference to a temporary (the converted / promoted variable), its type is a value type.
It cannot return a lvalue since it will have to implicitly promote the type of x to match the type of y (since both sides of : are not of the same type), and with that it has to create a temporary.
What does the standard say? (n1905)
Expressions 5.17 Assignment and compound assignment operators
5.17/3
If the second and third operand have different types, and either has (possibly cv-qualified) class type, 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 “reference to T2”, subject to the constraint that in the conversion the reference must bind directly (8.5.3) to E1.
— If E2 is an rvalue, or if the conversion above cannot be done:
— 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 an rvalue of type T2 that still refers to the original source class object (or the appropriate subobject thereof). [Note: that is, no copy is made. — end note] 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 non class 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 an rvalue (or the type it has, if E2 is an rvalue).
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. If neither can be converted, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, 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.
5.17/4
If the second and third operands are lvalues and have the same type, the result is of that type and is an lvalue and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.
5.17/5
Otherwise, the result is an rvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.
One more example
int x = 1;
int y = 2;
long z = 3;
(true ? x : y) = 100; // x & y are SAME type so it returns Lvalue(so it can be LHS of =). x = 100, y = 2
(false ? x : y) = 100; // x & y are SAME type so it returns Lvalue(so it can be LHS of =). x = 1 , y = 100
// (true ? x : z) = 100; // Error: x & z are DIFFERENT types so it returns Rvalue (so it can NOT be LHS of =)
// (false ? x : z) = 100; // Error: x & z are DIFFERENT types so it returns Rvalue (so it can NOT be LHS of =)

Conditional operator with same underlying class type

Should this program output 0 or 1? In my reading and understanding of the cited paragraphs from the C++14 standard, it should print 1, but both GCC and clang prints 0 (because the deduced type is A const instead of A const&):
#include <iostream>
struct A {};
int main()
{
A a;
A const& ra = std::move(a); // #1
std::cout << std::is_same<decltype(true ? ra : std::move(a)),
A const&>::value; // Prints 0
}
In this case, ra is a A const lvalue, and std::move(a) is a A xvalue, both of class-types. According to the standard about the conditional operator (emphasis mine), the result should be an lvalue of type A const, and thus the decltype result must be A const&:
[expr.cond]/3 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.
[...]
In this case, E2 is ra, which is a lvalue, and the other can be implicitely converted to "lvalue reference to T2", as shown in line // #1. "lvalue reference to T2" is translated as A const&, so, std::move(a) binds directly to a lvalue of type A const, and after the conversion, both operands have same type and value category, and thus:
[expr.cond]/3 If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category [...].
So, the operator result should be an lvalue and the decltype result should be a reference, and thus the program should print 1.
The question is awkwardly worded. You should instead ask what the type and value category of the expression true ? ra : std::move(a) should be. The answer to that question is a prvalue of type A const. This subsequently means the program should print 0, as I think every compiler correctly does.
The rules for ?: are fairly complex. In this case, we have two expressions of class type that we try to see if we can convert to each other, based on a limited subset of rules.
Attempting the conversion ra → std::move(a) fails. We first try with a target type is A&& which can't bind directly to ra. We then try the backup plan in (3.3.1) since the two expressions have the same underlying class type, but our target expression is not at least as cv-qualified as the source expression, so this also fails.
Attempting the conversion std::move(a) → ra fails (3.1) because we need to bind directly to an lvalue (we can bind an rvalue to a const lvalue reference, but here we are required to bind an lvalue). But, the (3.3.1) backup succeeds because now the target type is at least as cv-qualified as the source.
Hence, we apply the conversion and we continue as if the second operand were an lvalue of type A const but the third operand is now a prvalue of type A const (instead of an xvalue of type A).
(4) fails, because they're not of the same value category.
Hence, the result is a prvalue. And since they have the same type, the result is of that type: A const.

integral_constants in ternary operator

MSVC and clang/gcc disagree on whether two different integral constants can be used in a ternary operator (and as a consequence whether they have a common_type):
#include <utility>
int main()
{
return false
? std::integral_constant<int, 1>()
: std::integral_constant<int, 2>();
}
The above snippet compiles fine in clang and gcc, but not in MSVC. What is the correct behavior according to the standard? And if it's clang/gcc behavior then what are the conversion sequences used to deduce the common type of these two distinct types?
tldr; The code is well-formed. The conditional expression will have type int and value 2. This is an MSVC bug.
From [expr.cond]:
Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or [...], an attempt is made to form an implicit conversion sequence (13.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: [...]
— If E2 is 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 and T2 are the same class type (ignoring cv-qualification), or one is a base class of the other,
and T2 is at least as cv-qualified as T1, the target type is T2,
— otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue (4.1),
array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions.
So we're trying to form an implicit conversion sequence from type std::integral_constant<int, 1> to type std::integral_constant<int, 2>. That's not viable. Nor is the implicit conversion sequence in the reverse direction viable either. These types are simply not interconvertible.
So we continue:
If no conversion
sequence can be formed, the operands are left unchanged and further checking is performed as described
below. [...]
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, overload resolution is used to determine the conversions (if any) to be
applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed.
Ok, what overload resolution can we perform? From [over.match.oper]:
If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to
a type that is appropriate for a built-in operator.
Where the builtins are specified in [over.built] as:
For every pair of promoted arithmetic types L and R, there exist candidate operator functions of the form
LR operator?:(bool, L , R );
where LR is the result of the usual arithmetic conversions between types L and R.
One of those builtins would be int operator?:(bool, int, int). Since std::integral_constant<int, V> does have an operator int(), this is a viable conversion for both arguments.
We continue in [expr.cond]:
Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.
Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.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 result is of that type and the result object is initialized using the selected operand.
At this point, the second and third operands do have the same type: int. So the result object is initialized as an int, and the expression is well-formed.
The relevant paragraph from [expr.cond] is 6:
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, overload resolution is used to determine the conversions
(if any) to be applied to the operands (13.3.1.2, 13.6). If the
overload resolution fails, the program is ill-formed. Otherwise, the
conversions thus determined are applied, and the converted operands
are used in place of the original operands for the remainder of this
section.
integral_constant<int> has a conversion operator to int, so this can work.
Following through to 13.3.1.2, we see that by paragraph 3.2, all builtin ?: operators taking integer and floating point arguments are candidates.
Now overload resolution is performed for all these, given our three arguments. As per [over.ics.rank]/3.3, we tie-break by comparing the standard conversion sequences from int (the return type of integral_constant<int>'s conversion operator) to the builtin operators' parameter types.
However, a look at table 13 is enough; conversions to the floating point types have Conversion rank, and since int is a promoted type, converting to any integral type but int (which is an identity conversion) is an integral conversion with Conversion rank. Hence the best viable candidate is, unambiguously, operator?:(bool, int, int). That is, MSVC is wrong.

Conditional expression with class type lvalue operands

I'm trying to figure out what should be the value category of the result of a conditional expression if its second and third operands are lvalues of class type.
Example:
struct S {};
S x, y;
void foo(bool cond) {
cond ? x : y; // what is the value category of the result?
}
I'm seeing two different paragraphs in [expr.cond] say different things.
According to paragraph 4:
If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category
This paragraph seems to apply, and say that the result is an lvalue, because the second and third operands are both lvalues of the same type.
According to paragraph 6, however:
Lvalue-to-rvalue, array-to-pointer, and function-to-pointer 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 result is of that type. If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
This paragraph also seems to apply, and say that the result of the expression is an rvalue.
So which is it - lvalue or rvalue? Are the two paragraphs really contradicting each other, or am I missing something subtle?
You have to read the paragraphs in order, and select the first that applies. Emphasis mine.
1
...
2
If either the second or the third operand has type void ...
3
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 ...
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, overload resolution is used to determine the conversions (if any) to be
applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed.
Otherwise,
the conversions thus determined are applied, and the converted operands are used in place of the original
operands for the remainder of this section.
6
Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are per-
formed on the second and third operands. ...

Second standard conversion sequence of user-defined conversion

I have a misunderstanding about standard-conversion sequence terms. I have come across the following quote N3797 §8.5.3/5 [dcl.init.ref]:
— If the initializer expression
— is an xvalue (but not a bit-field), class prvalue, array prvalue or
function lvalue and “cv1 T1” is reference-compatible with “cv2
T2”, or
— has a class type (i.e., T2 is a class type), where T1 is not
reference-related to T2, and can be converted to an xvalue, class
prvalue, or function lvalue of type “cv3 T3”, where “cv1 T1” is
reference-compatible with “cv3 T3” (see 13.3.1.6),
[..]
In the second case, if the reference is an rvalue reference and the second standard conversion sequence of the user-defined conversion
sequence includes an lvalue-to-rvalue conversion, the program is
ill-formed.
What is the second standard conversion sequence here? I thought there is a standard conversion sequence including all necessary standard conversions (user-defined and implicit).
[over.ics.user]:
A user-defined conversion consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence. […]
The second standard conversion sequence converts the result of the
user-defined conversion to the target type for the sequence.
E.g. for
struct A
{
operator int();
};
void foo(short);
foo(A());
The second standard conversion sequence converts the int prvalue to the parameter of foo, that is, short. The first standard conversion sequence converts the A object to A (the implicit object parameter of operator int), which constitutes an identity conversion. For references, the rule quoted by you provides an example:
struct X {
operator B();
operator int&();
} x;
int&& rri2 = X(); // error: lvalue-to-rvalue conversion applied to
// the result of operator int&
Here, operator int& is selected by overload resolution. The return value is an lvalue reference to int. For that to initialize the reference, an lvalue-to-rvalue conversion would be necessary, and that's precisely what the quote prevents: Lvalues shall not be bound to rvalue references.
What the standard conversion term means is covered in the following clause of the C++ Standard:
4 Standard conversions [conv]
Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:
— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion,
and function-to-pointer conversion.
— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
— Zero or one qualification conversion.
[ Note: A standard conversion sequence can be empty, i.e., it can consist of no conversions. — end note ]
A standard conversion sequence will be applied to an expression if necessary to convert it to a required
destination type.
In other words, the standard conversion is a set of built-in rules the compiler can apply when converting one type to another. Those built-in conversions include:
No conversions
Lvalue-to-rvalue conversion
Array-to-pointer conversion
Function-to-pointer conversion
Qualification conversions
Integral promotions
Floating point promotion
Integral conversions
Floating point conversions
Floating-integral conversions
Pointer conversions
Pointer to member conversions
Boolean conversions
The standard conversion sequence can appear twice during the user-defined conversion sequence - either before and/or after the user-defined conversion:
§ 13.3.3.1.2 User-defined conversion sequences [over.ics.user]
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence. If the user-defined conversion is specified by a constructor (12.3.1), the initial standard conversion sequence converts the source type to the type required by the argument of the constructor. If the user-defined conversion is specified by a conversion function (12.3.2), the initial standard conversion sequence converts the source type to the implicit object parameter of the conversion function.
The second standard conversion sequence converts the result of the user-defined conversion to the target type for the sequence. Since an implicit conversion sequence is an initialization, the special rules for initialization by user-defined conversion apply when selecting the best user-defined conversion for a user-defined conversion sequence (see 13.3.3 and 13.3.3.1).
Having that said, for the following conversion:
A a;
B b = a;
the compiler will search for the conversion constructor in B that can take an instance of A (source type) through some initial standard conversion sequence so that it could then perform that user-defined conversion through selected constructor, and then apply another standard conversion - second standard conversion - for converting the resultant type of the user-defined conversion to the target type;
or:
the compiler will search for the conversion function in A that is callable after some initial standard conversion sequence of the implicit context, that could then convert an instance of A to some type convertible through another standard conversion - the second standard conversion - to the target type B.
As a tangible example let's consider the below conversion:
struct A
{
operator int() const;
};
A a;
bool b = a;
The compiler considers the following user-defined conversion sequence:
Initial standard conversion: Qualification conversion of A* to const A* to call const-qualified operator int() const.
User-defined conversion: conversion of A to int, through user-defined conversion function.
Second standard conversion: Boolean conversion of int to bool.
The case you are asking about can be split as follows:
struct A
{
operator int&();
};
int&& b = A();
The source type is A.
The target type is int&&.
The user-defined conversion sequence is the conversion of A to int&&.
The initial standard conversion sequence is No conversion at all.
The user-defined conversion is the conversion of A to int&.
The second standard conversion sequence (converting the result of the user-defined conversion to the target type) that is a part of the overall user-defined conversion sequence would be here the standard conversion of int& to int&& - an Lvalue-to-rvalue conversion. That conversion is considered since int& and int&& are reference-compatible types. According to the below statement §8.5.3 [dcl.init.ref]/p5:
[...] if the reference is an rvalue reference and the second standard conversion sequence of the user-defined conversion sequence includes an lvalue-to-rvalue conversion, the program is ill-formed.
that conversion is not applicable in the overall user-defined conversion sequence.