我如何处理“有符号/无符号不匹配”警告(C4018)?

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

迭代代码如下所示:

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

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

用一些unsignedtypesreplaceint是一个问题,因为我们经常使用OpenMP pragmas,它需要计数器为int

我要压制(数百个)警告,但是我恐怕错过了一些优雅的解决scheme。

在迭代器上 。 我认为在适当的地方应用迭代器是很好的。 我正在使用的代码将永远不会将随机访问容器更改为list或其他东西(所以迭代int i已经是容器不可知论者),并将始终需要当前的索引。 而所有你需要input的代码(迭代器本身和索引)只是使事情变得复杂,并且混淆了底层代码的简单性。

这一切都在你的things.size()types。 它不是int ,而是size_t (它存在于C ++中,不在C中),它等于一些“通常的”无符号types,即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); } 

这有一个整洁的好处,你的代码突然变成容器不可知论者。

而关于你的问题,如果你使用的某个库要求你使用int ,而unsigned int更适合,那么他们的API就是混乱的。 无论如何,如果你确定这些int总是正数,你可以做:

 int int_distance = static_cast<int>(distance); 

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

如果你不能/不会使用迭代器,并且如果你不能/不会使用std::size_t作为循环索引,那么创build一个.size()int转换函数来logging这个假设并明确地进行转换沉默编译器警告。

 #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) { ... } 

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

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

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

您可以使用:

  1. size_ttypes,以删除警告消息
  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()); } 

我有一个类似的问题。 使用size_t不起作用。 我尝试了另一个为我工作的人。 (如下)

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

我也可以为C ++ 11提出以下解决scheme。

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

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

我会给你一个更好的主意

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

decltype

检查实体的声明types或expression式的types和值类别。

所以,它推导出things.size()的types, i将是一个types与things.size()相同的types。 所以, i < things.size()将被执行没有任何警告

我会做的

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