我偶然发现了一种奇怪的行为(对我而言)。这是我的代码:
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 ++)?
我花了一天的时间弄清楚我的应用程序有什么问题。
答案 0 :(得分:1)
这似乎符合C ++标准。
摘自C++17 draft standard,第8.16节:
- 如果第二个或第三个操作数的类型为
void
,则[...]- 否则,如果第二和第三操作数是相同值类别的glvalue位字段,则[...]
否则,如果第二个操作数和第三个操作数具有不同的类型,并且具有(可能是cv限定的)类类型,则尝试从中形成隐式转换序列(16.3.3.1)这些操作数中的每一个与另一个的类型相同。 [...]尝试形成从类型
E1
的操作数表达式T1
到与操作数表达式{{1的类型T2
相关的目标类型的隐式转换序列}}如下
- 如果
E2
是左值,则目标类型是“对E2
的左值引用”,但要受约束,即转换中引用必须直接绑定到左值。- 如果
T2
是一个xvalue,则[...]- 如果
E2
是prvalue或以上两种转换序列均不能形成且至少一个操作数具有(可能是cv限定的)类类型
- 如果
E2
和T1
是同一类类型(忽略简历限定),则[...]- 否则,目标类型是
T2
在应用左值到右值,数组到指针和函数到指针标准转换后的类型。使用此过程,确定是否可以形成从第二操作数到为第三操作数确定的目标类型的隐式转换序列,反之亦然。如果可以形成两个序列,或者可以形成一个序列,但是这是模棱两可的转换序列,则程序格式不正确。如果无法形成转换序列,则操作数保持不变,并按如下所述进行进一步检查。否则,如果恰好可以形成一个转换序列,则该转换将应用于所选的操作数,并且在本节的其余部分中,将使用转换后的操作数代替原始操作数。
- 如果第二和第三操作数是具有相同值类别且具有相同类型的glvalue,则[...]
- 否则,结果为prvalue。如果第二个和第三个操作数的类型不同,并且都具有(可能是cv限定的)类类型,则[...]
左值到右值(7.1),数组到指针(7.2)和函数到指针(7.3)的标准转换是在第二和第三操作数上执行的。进行这些转换后,应满足以下条件之一:
- 第二和第三操作数具有相同的类型; [...]
- 第二和第三操作数具有算术或枚举类型;进行通常的算术转换以将它们转换为通用类型,结果就是该类型
[...]
E2
A a1;
auto c1 = b ? a1 : 0;
是类型0
的prvalue int
是类型a1
的左值规则4适用:
A
至E2 = 0
:尝试应用规则4.1。目标类型为a1
。但是,没有从A&
到左值int
的隐式转换。A&
至E2 = a1
:适用规则4.3.2。目标类型为0
。使用int
进行隐式转换。因此,遵循规则4,此条件表达式的返回类型为A::operator unsigned long long()
。
int
unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
是类型0
的prvalue int
是类型a2
的左值规则7.2适用:条件运算符的返回类型由两个表达式的算术转换规则确定。这里是unsigned long long
。
clang(demo)和g ++(demo)都将通过选项unsigned long long
发出警告。我没有MSVC的经验,但是它似乎也警告危险的算术转换:C4242,C4365。检查是否已启用它们。