左移是 C++11 中未定义行为的负面int
吗?
这里的相关标准段落来自5.8:
2/E1 <<E2 的值为 E1 左移 E2 位位置;空出 位为零填充。如果 E1 具有无符号类型,则 结果是 E1 × 2E2,比最大值多减模一 可在结果类型中表示。否则,如果 E1 具有有符号类型 和非负值,并且 E1×2E2 可在结果中表示 类型,然后是结果值;否则,行为为 定义。
让我感到困惑的部分是:
否则,如果 E1具有有符号类型和非负值,并且 E1×2E2 在结果类型中可表示,这就是结果值; 否则,行为是未定义的。
这是否应该被解释为左移任何负数是 UB? 或者这是否仅意味着如果您 LS 为负并且结果不适合结果类型,那么它就是 UB?
此外,前一条说:
1/换班操作员从左到右<<和>>组。 移位表达式: 加法表达 移位表达<<加法表达 移位表达>>加法表达
操作数应为整体或无作用域枚举类型,并且 执行整体促销。
结果的类型是提升的左操作数的类型。这 如果正确的操作数为负数或更大,则行为未定义 大于或等于提升的左操作数的长度(以位为单位)。
这明确表明对其中一个操作数使用负数是 UB。 如果是 UB 对另一个操作数使用负数,我希望在这里也能说清楚。
所以,底线是:
-1 << 1
未定义的行为?
@Angew提供了对标准语的伪法典解释,简洁地表达了一种可能的(可能的)有效解释。 其他人质疑这个问题是否真的是关于语言"行为未定义"的适用性,而不是我们(StackOverflow)使用短语"未定义的行为"。 这个编辑是为了进一步澄清我想问的问题。
@Angew对《标准》的解释是:
if (typeof(E1) == unsigned integral)
value = E1 * 2^E2 % blah blah;
else if (typeof(E1) == signed integral && E1 >= 0 && representable(E1 * 2^E2))
value = E1 * 2^E2;
else
value = undefined;
这个问题真正归结为——实际上是正确的解释:
value = E1 left-shift-by (E2)
switch (typeof(E1))
{
case unsigned integral :
value = E1 * 2^E2 % blah blah;
break;
case signed integral :
if (E1 >= 0)
{
if (representable(E1 * 2^E2))
{
value = E1 * 2^E2;
}
else
{
value = undefined;
}
}
break;
}
?
旁注,从 psudocode 的角度来看待这个问题,我心中很清楚@Agnew的解释是正确的。
是的,我会说它是未定义的。如果我们将标准代码转换为伪代码:
if (typeof(E1) == unsigned integral)
value = E1 * 2^E2 % blah blah;
else if (typeof(E1) == signed integral && E1 >= 0 && representable(E1 * 2^E2))
value = E1 * 2^E2;
else
value = undefined;
我想说的是,他们之所以明确使用右操作数而不是左操作数,是因为您引用的 paragrpah(带有右侧操作数大小写的参数)适用于左移位和右移位。
对于左侧操作数,规则不同。左移负数是未定义的,右移是实现定义的。
这是否应该被解释为左移任何负数是 UB?
是的,当给定任何负数时,行为是未定义的。仅当满足以下两个条件时,才会定义该行为:
- 数字为非负数
- E1 × 2E2 可在结果类型中表示
从字面上看,这就是"如果 E1 具有有符号类型和非负值,并且 E1×2E2 在结果类型中可表示,那么这就是结果值;否则,行为是未定义的,"他说:
if X and Y
then Z
else U
根据问题回答:
真正的问题是:
我们是否可以将术语"行为未定义"完全等同于术语"未定义的行为"。
按照目前的措辞,它的意思是"未定义的行为"。
个人对情况的评论
但我不相信这是作者的意图.
如果这是作者的意图,那么我们可能应该有一个注释来解释原因。但我更倾向于相信作者的意思是该操作的结果是未定义的,因为负数的表示没有明确定义由标准定义。如果没有为负数显式定义负数的表示形式,则移动位将导致未定义的值。
无论哪种方式,措辞(或解释)都需要收紧/扩展,以减少歧义。