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.
Related
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.
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. ...
N3797 §13.3.3.1 [over.best.ics] says:
The sequence of conversions is an implicit conversion as defined in
Clause 4 [...]
However, clause 4 defines the following list of the 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
Integer conversion rank
Consider the following example:
#include <iostream>
using namespace std;
struct A
{
operator int()
{
return 42;
}
};
A a;
int b = a; //User-defined conversion sequence applied
int main() { }
As long as user-defined conversion doesn't belong to a set of standard conversions, there is no any standard conversion being applied in the example. So what is the sense of the quote I provided?
The rest of the quote you omitted might be illuminating:
§13.3.3.1/1 An implicit conversion sequence is a sequence of
conversions used to convert an argument in a function call to the type
of the corresponding parameter of the function being called. The
sequence of conversions is an implicit conversion as defined in Clause
4, which means it is governed by the rules for initialization of an
object or reference by a single expression (8.5, 8.5.3).
Clause 4 does indeed talk about this.
3 An expression e can be implicitly converted to a type T if and
only if the declaration T t=e; is well-formed, for some invented
temporary variable t (8.5).
6 The effect of any implicit conversion is the same as performing the
corresponding declaration and initialization and then using the
temporary variable as the result of the conversion. The result is an
lvalue if T is an lvalue reference type or an rvalue reference to
function type (8.3.2), an xvalue if T is an rvalue reference to
object type, and a prvalue otherwise. The expression e is used as a
glvalue if and only if the initialization uses it as a glvalue.
The "full set of such conversions" (listed in Clause 4) refers to standard conversions. Remember that it says standard conversion sequences can be empty. Then §13.3.3.1.2 describes user-defined conversion sequences. It consists of:
A standard conversion sequence
A user-defined conversion
Another standard conversion sequence
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.
In the following example, which conversion function should be called? Why should that one be chosen over the other?
struct A
{
operator int();
operator int*();
};
A x;
int i = x + 1;
The compiler chooses operator int().. but why?
Here are some relevant quotes from C++03:
From [expr.add]
For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type.
From [conv]
expressions with a given type will be implicitly converted to other types in several contexts:
When used as operands of operators. The operator’s requirements for its operands dictate the destination type
The reason for this behavior is that the built-in operator which accepts a pointer as its left hand operand accepts an object of type std::ptrdiff_t as its right hand operand. This is specified in § 13.6 of the C++11 Standard:
For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form
T * operator+(T *, std::ptrdiff_t);
[...]
Since 1 has type int, the compiler considers the built-in operator + that takes two ints as a better choice, because it onlys require a (user-defined) conversion for the first argument.
If you provided an argument of type std::ptrdiff_t as the right hand operand of operator +, you would see the expected ambiguity:
int i = x + static_cast<std::ptrdiff_t>(1); // AMBIGUOUS!
Here is a live example.