为什么1 << 31改为在C++ 14中实现定义?

M.M*_*M.M 32 c++ bit-shift c++14

在2014年之前的所有C和C++版本中,写作

1 << (CHAR_BIT * sizeof(int) - 1)
Run Code Online (Sandbox Code Playgroud)

导致未定义的行为,因为左移定义为等效于连续乘法2,并且此移位导致有符号整数溢出:

结果E1 << E2E1左移位E2位置; 腾出的位用零填充.[...]如果E1有一个带符号的类型和非负值,并且结果类型中可以表示E1×2 E2,那么这就是结果值; 否则,行为未定义.

但是在C++ 14中,文本已经改变<<但不是为了乘法:

E1 << E2E1左移位E2位置; 空位是零填充的.[...]否则,如果E1有一个有符号类型和非负值,并且E1×2 E2可以在结果类型的相应无符号类型中表示,那么转换为结果类型的那个值就是结果值; 否则,行为未定义.

现在的行为与签名类型的超出范围分配相同,即[conv.integral]/3所涵盖的行为:

如果目标类型已签名,则如果可以在目标类型(和位字段宽度)中表示该值,则该值不会更改; 否则,该值是实现定义的.

这意味着它仍然是不可移植的1 << 31(在具有32位int的系统上).那么为什么在C++ 14中做出这种改变呢?

T.C*_*.C. 18

相关问题是CWG 1457,其中的理由是更改允许1 << 31在常量表达式中使用:

5.8 [expr.shift]第2段的当前措辞使得通过将(带符号)1左移到符号位来创建给定类型的最负整数,这是未定义的行为,即使这并不常见并且有效正确地对大多数(二进制补码)架构:

...如果E1具有带符号类型和非负值,并且E1*2 E2在结果类型中可表示,那么这就是结果值; 否则,行为未定义.

因此,此技术不能用于常量表达式,这将破坏大量代码.

常量表达式不能包含未定义的行为,这意味着在需要常量表达式的上下文中使用包含UB的表达式会使程序格式错误.numeric_limits::min例如,libstdc ++ 因为这个原因而无法在clang中编译.

  • +1 Fwiw,libc ++根据`min()`计算`max()`,因此用'max()`写`min()`本来就太有趣了.:-)就我而言,最重要的是:我已经厌倦了站在我的头上来支持那些不是**现代C++编译器所针对的架构.两个补码有符号整数是我们现在和将来的现实. (10认同)
  • @Loopunroller:问题中的最后一个标准引用是为了避免要求将无符号值转换为带符号的二进制补码规则.而且,对于两个补语来说,只有一个合理的结果是不正确的.饱和算术也是一个有用的,理智的选择.人们甚至可以说这比正面价值加倍造成负面影响更加明智. (3认同)