如何处理"signed/unsigned mismatch"警告 (C4018)?

How do I deal with "signed/unsigned mismatch" warnings (C4018)?

本文关键字:mismatch 警告 C4018 unsigned 处理 何处理 signed      更新时间:2023-10-16

我使用了大量用c++编写的计算代码,考虑到高性能和低内存开销。它大量使用STL容器(主要是std::vector),并且几乎在每个函数中都要遍历这些容器。

迭代代码如下:

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

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

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

我正准备删掉(数百个)警告,但恐怕我错过了一些解决问题的优雅方法。

在迭代器

。我认为在适当的地方应用迭代器是很棒的。我正在使用的代码将永远不会将随机访问容器更改为std::list或其他东西(因此与int i迭代已经是容器无关的),并且总是需要当前索引。您需要键入的所有附加代码(迭代器本身和索引)只会使事情变得复杂,并混淆底层代码的简单性。

这都是你的things.size()类型。它不是int,而是size_t(它存在于c++中,而不是C中),它等于一些"常见的"无符号类型,即x86_32的unsigned int

操作符"less"(<)不能应用于两个不同符号的操作数。只是没有这样的操作码,标准也没有规定编译器是否可以进行隐式符号转换。因此,它只是将有符号数视为无符号数并发出警告。

正确的写法是

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

或者更快的

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

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

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);
}

这个a有一个很好的好处,那就是你的代码突然变得与容器无关了。

关于你的问题,如果你使用的一些库要求你使用int,而unsigned int更适合,他们的API是混乱的。无论如何,如果你确信这些int总是阳性的,你可以这样做:

int int_distance = static_cast<int>(distance);

将清楚地向编译器指定你的意图:它不会再向你发出警告。

如果不能/不愿使用迭代器,并且不能/不愿使用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()一样快。这两个版本都不会产生编译器警告,并且它只是对惯用循环进行了轻微的修改。

如果您也想在发布版本中捕获假设失败,您可以用If语句替换断言,该语句抛出类似std::out_of_range("container size exceeds range of int")的内容。

注意,这既解决了有符号/无符号比较,也解决了潜在的sizeof(int) != sizeof(Container::size_type)问题。您可以让所有警告保持启用状态,并使用它们来捕获代码其他部分中的真正错误。

您可以使用:

  1. size_t类型,删除警告消息
  2. 迭代器+距离(就像第一个提示)
  3. 只有迭代器
  4. 函数对象
例如:

// 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());
}

c++ 20现在有std::cmp_less

在c++20中,有标准的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作为int整数,循环,并且需要与无符号整数进行比较,可以这样做:

#include <utility> // std::cmp_less
for (int i = 0; std::cmp_less(i, things.size()); ++i)
{
    // ...
}

这也涵盖了如果我们错误地将-1(即int) static_cast转换为unsigned int的情况。这意味着,以下代码不会给你一个错误:

static_assert(1u < -1);

但是使用std::cmp_less

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

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

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

(c++不够聪明,不能自动p = 0,所以我必须设置p = 0U....)

我会给你一个更好的主意

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

decltype is

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

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

我也遇到过类似的问题。使用size_t不能正常工作。我试了另一个对我有效的。(如下)

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

我就写

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