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

我使用大量用 编写的计算代码工作,考虑到高性能和低内存开销。它经常使用 STL 容器(主要是 std::vector) ,并且几乎在每个函数中迭代该容器。

迭代代码如下所示:

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

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

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

我即将去掉(数百个)警告,但恐怕我错过了一些解决问题的优雅方案。

关于迭代器。我认为迭代器在适当的地方应用是非常好的。我正在处理的代码将 永远不会将随机访问容器更改为 std::list或其他内容(因此使用 int i迭代已经是不可知的容器) ,并且 一直都是将需要当前索引。您需要键入的所有附加代码(迭代器本身和索引)只会使问题复杂化,并使底层代码的简单性变得模糊。

115505 次浏览

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

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 有一个很好的优点,那就是您的代码突然变得与容器无关。

关于您的问题,如果您使用的一些库要求您在 unsigned int更适合的地方使用 int,那么它们的 API 就是混乱的。无论如何,如果你确定那些 int总是正的,你可以这样做:

int int_distance = static_cast<int>(distance);

它会向编译器清楚地说明你的意图: 它不会再用警告来烦你了。

你可使用:

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

都是你的 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) { /**/ }

如果你不能/不愿意使用迭代器,如果你不能/不愿意使用 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)问题。您可以启用所有警告,并使用它们在代码的其他部分捕获真正的 bug。

我也有过类似的问题。使用 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] << ' ';

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

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


}

(C + + 对于 auto p = 0来说不够聪明,所以我必须把 p = 0U...)

我会给你一个更好的主意

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

decltype

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

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

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-1(即 int)转化为 unsigned int。这意味着,下面的代码不会给出错误:

static_assert(1u < -1);

std::cmp_less的使用将

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