【问题标题】:Why does this conditional operator evaluate to int?为什么这个条件运算符的计算结果为 int?
【发布时间】:2019-06-27 17:21:33
【问题描述】:

我偶然发现了一个奇怪的(对我来说)行为。这是我的代码:

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 longc2?为什么没有生成转换警告(VC++)?

我花了一天的时间才弄清楚我的申请出了什么问题。

【问题讨论】:

  • @πάνταῥεῖ 我相信重复问题的答案不能回答这里提出的问题。它们指的是值类型和提及转换,但没有解释为什么在这种情况下unsigned long long被转换为int而不是int提升到@987654329 @。投票重新开放,除非您提供指向链接问题答案所在位置的指针。
  • 试试这个:auto c1 = b ? a1 : 0ull;

标签: c++ conditional-operator conversion-operator


【解决方案1】:

这似乎符合 C++ 标准。

C++17 标准

来自C++17 draft standard,第 8.16 节:

  1. 如果第二个或第三个操作数的类型为void,[...]
  2. 否则,如果第二个和第三个操作数是相同值类别的左值位域,[...]
  3. 否则,如果第二个和第三个操作数具有不同的类型并且任何一个具有(可能是 cv 限定的)类类型,[...] 将尝试从其中的每一个中形成一个隐式转换序列 (16.3.3.1)其他类型的操作数。 [...] 尝试形成从 T1 类型的操作数表达式 E1 到与操作数表达式 E2 的类型 T2 相关的目标类型的隐式转换序列,如下所示

    1. 如果E2 是左值,则目标类型是“对T2 的左值引用”,受制于在转换中引用必须直接绑定到左值的约束
    2. 如果 E2 是一个 xvalue,[...]
    3. 如果E2 是纯右值,或者上述转换序列都不能形成,并且至少有一个操作数具有(可能是 cv 限定的)类类型
      1. 如果 T1T2 是相同的类类型(忽略 cv-qualification),[...]
      2. 否则,目标类型是E2 在应用左值到右值、数组到指针和函数到指针标准转换后的类型。

    使用此过程,确定是否可以形成从第二个操作数到为第三个操作数确定的目标类型的隐式转换序列,反之亦然。如果两个序列都可以形成,或者一个可以形成但它是模棱两可的转换序列,则程序是非良构的。如果不能形成转换序列,则操作数保持不变,并按如下所述执行进一步检查。否则,如果恰好可以形成一个转换序列,则该转换将应用于所选操作数,并在本节的其余部分中使用转换后的操作数代替原始操作数。

  4. 如果第二个和第三个操作数是相同值类别的泛左值并且具有相同的类型,[...]
  5. 否则,结果为纯右值。如果第二个和第三个操作数的类型不同,并且都具有(可能是 cv 限定的)类类型,[...]
  6. 左值到右值 (7.1)、数组到指针 (7.2) 和函数到指针 (7.3) 标准转换是在第二个和第三个操作数上执行的。在这些转换之后,应满足以下条件之一:

    1. 第二个和第三个操作数的类型相同; [...]
    2. 第二和第三个操作数具有算术或枚举类型;执行通常的算术转换以将它们转换为通用类型,结果就是该类型

    [...]

第一种情况:

A a1;
auto c1 = b ? a1 : 0;
  • 0int 类型的纯右值
  • a1A 类型的左值

第 4 条适用:

  • E1=0 到 E2=a1:尝试应用规则 4.1。目标类型是A&amp;。但是,没有从 int 到左值 A&amp; 的隐式转换。
  • E1=a1 到 E2=0:适用规则 4.3.2。目标类型是int。有一个隐式转换,使用A::operator unsigned long long()

因此遵循规则4,该条件表达式的返回类型为int

第二种情况:

unsigned long long a2 = 1ull << 32;
auto c2 = b ? a2 : 0;
  • 0int 类型的纯右值
  • a2unsigned long long 类型的左值

规则 7.2 适用:条件运算符的返回类型由两个表达式的算术转换规则确定。这里是unsigned long long

关于编译器警告

clang (demo) 和 g++ (demo) 都会使用选项 -Wconversion 发出警告。我没有使用 MSVC 的经验,但它似乎也有危险算术转换的警告:C4242C4365。检查您是否已启用它们。

【讨论】:

  • 感谢您的澄清。我发现 MSVC 确实会创建警告 C4244(警告级别 3)但仅当“启用内部功能”设置为否时。这不是我日常构建的情况。
猜你喜欢
  • 2018-03-08
  • 1970-01-01
  • 2011-08-19
  • 2023-02-07
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多