在模板中表达左移或右移的优雅方式

MSa*_*ers 5 c++

我目前有一个模板功能,根据其模板参数A和B,可以向左或向右移动一个值:

template <int A, int B> void f(X) {
// ...
if (A >= B)
{
  SetValue(X << (A-B));
}
else // (A < B)
{
  SetValue(X >> (B-A));
}
Run Code Online (Sandbox Code Playgroud)

当我实例化模板时A<B,我在(无法到达)第一个分支上得到一个负向移位警告,否则我会收到第一个分支上留下负移位的警告.我们的代码库没有警告,所以这是不可接受的.这两个班次陈述是否有简洁易读的替代方案?

类似的问题(例如,向左或向右动态移动)没有这种虚假警告,因为移位距离是那里的运行时变量.

For*_*veR 5

使用C++ 11或boost.

template<int A, int B>
void f_impl(typename std::enable_if<(A >= B)>::type* = 0)
{
   // first case
}

template<int A, int B>
void f_impl(typename std::enable_if<(A < B)>::type* = 0)
{
   // second case
}

template<int A, int B>
void f()
{
   f_impl<A, B>();
}
Run Code Online (Sandbox Code Playgroud)

  • 对一个简单问题的复杂回答......只需使用绝对值即可. (2认同)

dav*_*mac 4

将 (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)));
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意 解决有关未定义行为的各种评论:此提议的解决方案可能导致未定义行为的唯一意义是通过执行大于操作数位宽的量的移位。然而,这是通过比较来保护的;假设 A 和 B 之间的差异是问题中隐含的安全移位计数,则if (A >= B)确保只有具有该数量的移位实际执行。该if语句的另一个分支执行,因此不执行移位,并且不能从移位中产生未定义的行为(尽管如果执行它,它肯定会这样做)。

一些评论者断言,未执行的分支仍然可能导致未定义的行为。我有点不明白为什么会发生这样的误解。考虑以下代码:

int *a = nullptr;

if (a != nullptr) {
    *a = 4;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果空指针的取消引用即使未执行也会导致未定义的行为,则保护条件将变得毫无用处。事实显然并非如此。上面的代码完全没问题;它分配a一个值,然后由于保护nullptr而不会取消引用。a尽管这种明显的示例(分配给 null 后立即检查 null)往往不会出现在实际代码中,但“受保护的取消引用”通常是一种常见的习惯用法。如果检查的指针实际上为空,它本身当然不会产生未定义的行为;这就是守卫有用的原因。

  • 我想你受编译器的摆布。您可以尝试将 AB 结果与某些内容进行 AND 运算(例如 0x1F 表示 32 位限制)。 (2认同)