如何处理“签名/未签名的不匹配”警告(C4018)?

时间:2011-04-19 09:05:03

标签: c++ refactoring

我使用大量用C ++编写的计算代码,考虑到高性能和低内存开销。它经常使用STL容器(大多数是vector),并且几乎在每个函数中遍历那些容器。

迭代代码如下所示:

for (int i = 0; i < things.size(); ++i)
{
    // ...
}

但它会产生签名/无符号不匹配警告(Visual Studio中的C4018)。

使用某些int类型替换unsigned是一个问题,因为我们经常使用OpenMP pragma,并且它要求计数器为int

我即将压制(数百个)警告,但我担心我错过了一些优雅的解决方案。

在迭代器上。我认为在适当的地方应用迭代器很棒。我正在使用的代码永远不会将随机访问容器更改为list或其他东西(因此使用int i进行迭代已经是容器不可知的),并且总是 需要当前索引。您需要键入的所有其他代码(迭代器本身和索引)只会使问题复杂化并模糊底层代码的简单性。

9 个答案:

答案 0 :(得分:52)

全部都在您的things.size()类型中。它不是int,而是size_t(它存在于C ++中,而不是C中),它等于某些“通常”的无符号类型,即x86_32的unsigned int

运算符“less”(&lt;)不能应用于两个不同符号的操作数。没有这样的操作码,并且标准没有指定,编译器是否可以进行隐式符号转换。所以它只是将签名号码视为未签名并发出警告。

一样写它是正确的
for (size_t i = 0; i < things.size(); ++i) { /**/ }

甚至更快

for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }

答案 1 :(得分:13)

理想情况下,我会使用这样的结构:

for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
  // if you ever need the distance, you may call std::distance
  // it won't cause any overhead because the compiler will likely optimize the call
  size_t distance = std::distance(things.begin(), i);
}

这样做的优点是你的代码突然变得与容器无关。

关于您的问题,如果您使用的某个库要求您使用int最适合unsigned int,那么他们的API就会变得混乱。无论如何,如果您确定那些int总是积极的,您可以这样做:

int int_distance = static_cast<int>(distance);

这将明确指出您对编译器的意图:它不会再向您发出警告。

答案 2 :(得分:7)

如果您不能/不会使用迭代器,并且如果您不能/不会将std::size_t用于循环索引,请进行.size()int转换功能记录该假设并明确转换以使编译器警告静音。

#include <cassert>
#include <cstddef>
#include <limits>

// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
template <typename ContainerType>
/* constexpr */ int size_as_int(const ContainerType &c) {
    const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
    assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
    return static_cast<int>(size);
}

然后你就像这样编写你的循环:

for (int i = 0; i < size_as_int(things); ++i) { ... }

这个函数模板的实例化几乎肯定会被内联。在调试版本中,将检查该假设。在发布版本中,它不会,并且代码将像您直接调用size()一样快。这两个版本都不会产生编译器警告,只是对惯用循环进行了轻微修改。

如果你想在发布版本中捕获假设失败,你可以用一个抛出类似std::out_of_range("container size exceeds range of int")之类的if语句来替换断言。

请注意,这解决了签名/无符号比较以及潜在的sizeof(int)!= sizeof(Container::size_type)问题。您可以启用所有警告并使用它们来捕获代码其他部分的实际错误。

答案 3 :(得分:6)

您可以使用:

  1. size_t type,删除警告消息
  2. 迭代器+距离(如第一次提示)
  3. 只有迭代器
  4. 功能对象
  5. 例如:

    // simple class who output his value
    class ConsoleOutput
    {
    public:
      ConsoleOutput(int value):m_value(value) { }
      int Value() const { return m_value; }
    private:
      int m_value;
    };
    
    // functional object
    class Predicat
    {
    public:
      void operator()(ConsoleOutput const& item)
      {
        std::cout << item.Value() << std::endl;
      }
    };
    
    void main()
    {
      // fill list
      std::vector<ConsoleOutput> list;
      list.push_back(ConsoleOutput(1));
      list.push_back(ConsoleOutput(8));
    
      // 1) using size_t
      for (size_t i = 0; i < list.size(); ++i)
      {
        std::cout << list.at(i).Value() << std::endl;
      }
    
      // 2) iterators + distance, for std::distance only non const iterators
      std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
      for ( ; itDistance != endDistance; ++itDistance)
      {
        // int or size_t
        int const position = static_cast<int>(std::distance(list.begin(), itDistance));
        std::cout << list.at(position).Value() << std::endl;
      }
    
      // 3) iterators
      std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
      for ( ; it != end; ++it)
      {
        std::cout << (*it).Value() << std::endl;
      }
      // 4) functional objects
      std::for_each(list.begin(), list.end(), Predicat());
    }
    

答案 4 :(得分:3)

我还可以为C ++ 11提出以下解决方案。

for (auto p = 0U; p < sys.size(); p++) {

}

(对于自动p = 0,C ++不够智能,所以我必须把p = 0U ....)

答案 5 :(得分:3)

我会给你一个更好的主意

for(decltype(things.size()) i = 0; i < things.size(); i++){
                   //...
}

decltype

  

检查实体的声明类型或类型和值类别   表达。

因此,它推导出things.size()的类型,而i的类型与things.size()相同。所以, i < things.size()将在没有任何警告的情况下执行

答案 6 :(得分:1)

我有类似的问题。使用size_t无效。我尝试了另一个适合我的方法。 (如下)

for(int i = things.size()-1;i>=0;i--)
{
 //...
}

答案 7 :(得分:0)

我会做的

int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
    cout << primeNumber[i] << ' ';

答案 8 :(得分:0)

C++20 现在有 std::cmp_less

中,我们有标准的constexpr函数

std::cmp_equal
std::cmp_not_equal
std::cmp_less
std::cmp_greater
std::cmp_less_equal
std::cmp_greater_equal

<utility> 标头中添加,正是针对此类场景。

<块引用>

比较两个整数 tu 的值。与内置比较运算符不同,负有符号整数总是比较小于(且不等于)无符号整数:这种比较对于有损整数转换是安全的

这意味着,如果(由于某些有线原因)必须将 i 用作 integer、循环,并且需要与无符号整数进行比较,则可以这样做:

#include <utility> // std::cmp_less

for (int i = 0; std::cmp_less(i, things.size()); ++i)
{
    // ...
}

如果我们错误地将static_cast(即-1intunsigned int,这也适用于这种情况。这意味着,以下不会给你一个错误:

static_assert(1u < -1);

但是 std::cmp_less 的用法会

static_assert(std::cmp_less(1u, -1)); // error