【问题标题】:Getting rid of gcc shift by negative warning通过负面警告摆脱 gcc shift
【发布时间】:2008-11-06 00:15:14
【问题描述】:

我有一些看起来像这样的代码:

template<unsigned int A, unsigned int B>
int foo() {
  int v = 1;
  const int x = A - B;
  if (x > 0) {
    v = v << x;
  }
  bar(v);
}

gcc 会抱怨 x 对于 A、B 的某些实例是负数;但是,我确实进行了检查以确保它是非负数。解决这个问题的最佳方法是什么?我知道我可以将x 转换为unsigned int,但这会导致警告x 大于v 的宽度(因为它会将负数转换为正数)。我知道有一种解决方法涉及创建一个新的模板化 shift 函数,但如果可能的话,我想避免这种情况。

【问题讨论】:

    标签: c++ gcc


    【解决方案1】:

    由于 A 和 B 在编译时已知,因此您不仅可以摆脱警告,还可以摆脱运行时 if,无需任何强制转换,如下所示:

    #include <iostream>
    using namespace std;
    
    template< unsigned int A, unsigned int B >
    struct my
    {
        template< bool P >
        static void shift_if( int & );
    
        template<>
        static void shift_if< false >( int & ) {}
    
        template<>
        static void shift_if< true >( int & v ) { v <<= A - B; }
    
        static void op( int & v ) { shift_if< (A > B) >( v ); }
    };
    
    template< unsigned int A, unsigned int B >
    int foo()
    {
        int v = 1;
        my< A, B >::op( v );
        return v;
    }
    
    int main() {
        cout << foo< 1, 3 >() << endl;
        cout << foo< 3, 1 >() << endl;
        cout << foo< 300, 1 >() << endl;
        cout << foo< 25, 31 >() << endl;
        return 0;
    }
    

    【讨论】:

    • 一个非常好的解决方案。尽管它需要调整以解决 (A - B >= 32) 问题。顺便说一句,if 无论如何都可能被排除,因为编译器可以检测到它永远不会发生。
    • @Evan Teran,谢谢!我注意到 shift-wrap 问题,但 OP 的原始代码让它换行,所以我认为这就是他想要的。我怀疑您对编译器优化的看法是正确的,但在我的版本中,表达式 A - B 是无符号的,因此避免了警告。
    • 理想情况下,如果该数字为正数,我希望代码在我移动过多时警告我,如果为负数,则根本不警告我(因为它不会被执行) .我想没有办法绕过它,但这种方式。很高兴知道。
    • 您可以通过声明而不是定义额外的特化来使过度的正偏移成为错误。
    【解决方案2】:

    为什么不将 x 设为 unsigned char 类型并强制转换呢?您确定不需要移动超过 255 位吗?

    const unsigned char x = static_cast<unsigned char>(A - B);
    

    或者也许使用掩码来确保移位在这样的范围内:

    const unsigned int x = static_cast<unsigned int>(A - B) & 0x1f; // limit A-B to have a range of (0 - 31)
    

    编辑:

    作为对评论的回应,这里有一个想法:

    template<unsigned int A, unsigned int B>
    int foo() {
      int v = 1;
      const int x = A - B;
      if (x > 0) {
        v = v << (static_cast<unsigned int>(x) & 0x1f);
      }
      bar(v);
    }
    

    注意:您可以将 0x1f 替换为:(CHAR_BIT * sizeof(T) - 1)

    编辑:响应最新评论,此代码不会发出任何警告编译:g++ -W -Wall -ansi -pedantic test.cc -o test

    #include <iostream>
    
    template<unsigned int A, unsigned int B>
    int foo() {
      int v = 1;
      const int x = A - B;
      if (x > 0) {
        v = v << (static_cast<unsigned int>(x) & 0x1f);
      }
      return v;
    }
    
    int main() {
        std::cout << foo<1, 3>() << std::endl;
        std::cout << foo<3, 1>() << std::endl;
        std::cout << foo<300, 1>() << std::endl;
        std::cout << foo<25, 31>() << std::endl;
    }
    

    【讨论】:

    • 我相信如果右侧是负数,它会将其转换为unsigned int,这将使其成为正数。如果A-B 的结果是否定的,我不想运行代码。
    • 因为它知道编译时的值,所以任何大于 32 的数字都会标记另一个警告,即移位大于类型宽度的数字。因此,虽然这确实解决了负面问题,但它会引发第二个警告。
    • 我将值限制为0-32,所以应该没有警告
    • 啊,对不起,我错过了那个面具。我以为它掩盖了负号。
    【解决方案3】:

    这行得通吗?

    const short unsigned int x = A - B;
    

    它切断的位比需要切断的多得多,但如果你的 A - B 值足够小......

    【讨论】:

    • 更具体地说:我有A == 25B == 31。这给了我x == -6 看起来像111...11010 并且截断它仍然会给我一个相当大的数字
    猜你喜欢
    • 1970-01-01
    • 2012-11-10
    • 2016-01-15
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    相关资源
    最近更新 更多