为什么此条件运算符的计算结果为 int?
Why does this conditional operator evaluate to int?
我偶然发现了一个奇怪的(对我来说)行为。这是我的代码:
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;
为什么 c1
属于 int
而不是 unsigned long long
类似于 c2
?为什么没有生成转换警告(VC++)?
我花了一天时间才弄清楚我的应用程序出了什么问题。
这似乎符合 C++ 标准。
C++17 标准
来自 C++17 draft standard,第 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
[...]
第一种情况:
A a1;
auto c1 = b ? a1 : 0;
0
是 int
类型的纯右值
a1
是 A
类型的左值
规则 4 适用:
- E1=
0
到 E2=a1
:尝试应用规则 4.1。目标类型是 A&
。但是,没有从 int
到左值 A&
. 的隐式转换
- E1=
a1
到 E2=0
:适用规则 4.3.2。目标类型是 int
。有一个隐式转换,使用 A::operator unsigned long long()
.
因此根据规则 4,此条件表达式的 return 类型为 int
.
第二种情况:
unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
0
是 int
类型的纯右值
a2
是 unsigned long long
类型的左值
规则7.2适用:条件运算符的return类型由两个表达式的算术转换规则决定。这是 unsigned long long
.
关于编译器警告
两个 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。检查你是否启用了它们。
我偶然发现了一个奇怪的(对我来说)行为。这是我的代码:
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;
为什么 c1
属于 int
而不是 unsigned long long
类似于 c2
?为什么没有生成转换警告(VC++)?
我花了一天时间才弄清楚我的应用程序出了什么问题。
这似乎符合 C++ 标准。
C++17 标准
来自 C++17 draft standard,第 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
是int
类型的纯右值
a1
是A
类型的左值
规则 4 适用:
- E1=
0
到 E2=a1
:尝试应用规则 4.1。目标类型是A&
。但是,没有从int
到左值A&
. 的隐式转换
- E1=
a1
到 E2=0
:适用规则 4.3.2。目标类型是int
。有一个隐式转换,使用A::operator unsigned long long()
.
因此根据规则 4,此条件表达式的 return 类型为 int
.
第二种情况:
unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
0
是int
类型的纯右值
a2
是unsigned long long
类型的左值
规则7.2适用:条件运算符的return类型由两个表达式的算术转换规则决定。这是 unsigned long long
.
关于编译器警告
两个 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。检查你是否启用了它们。