将 (AB) 和 (BA) 的结果转换为无符号,并另外用(sizeof(int) - 1)
. 这将清除 GCC 5.5 和 6.3 的警告。对于 GCC 的更新版本,不会生成警告。
template <int A, int B> void f(int X) {
// ...
if (A >= B)
{
SetValue(X << ((unsigned)(A-B) & (sizeof(int) - 1)));
}
else // (A < B)
{
SetValue(X >> ((unsigned)(B-A) & (sizeof(int) - 1)));
}
}
请注意
解决有关未定义行为的各种评论:此提议的解决方案可能导致未定义行为的唯一意义是执行大于操作数位宽的量的移位。但是,这是通过比较来保护的;假设 A 和 B 之间的差异是问题中隐含的安全移位计数,则if (A >= B)
确保只有具有该数量的移位实际执行。if
语句的另一个分支不执行,因此不执行移位,并且不能从移位中产生未定义的行为(尽管如果它被执行,它肯定会这样做)。
一些评论者断言,未执行的分支仍然会导致未定义的行为。我有点不知所措,不知道怎么会出现这样的误解。考虑以下代码:
int *a = nullptr;
if (a != nullptr) {
*a = 4;
}
现在,如果一个空指针的取消引用导致未定义的行为,即使它没有被执行,保护条件也变得无用。显然不是这样。上面的代码非常好;由于守卫,它分配a
了一个值nullptr
,然后不取消引用。a
尽管这种明显的例子(赋值为 null 后立即检查 null)不会出现在实际代码中,但“受保护的取消引用”通常是一种常见的习惯用法。如果检查的指针实际为空,它本身当然不会产生未定义的行为;这就是警卫有用的原因。