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

发布于 2024-11-02 15:37:25 字数 767 浏览 1 评论 0原文

我使用 考虑到高性能和低内存开销。它大量使用 STL 容器(主要是 std::vector),并且几乎在每个函数中都会迭代该容器。

迭代代码如下所示:

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

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

用一些 unsigned 类型替换 int 是一个问题,因为我们经常使用 OpenMP 编译指示,并且它要求计数器为 int代码>.

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

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

I work with a lot of calculation code written in with high-performance and low memory overhead in mind. It uses STL containers (mostly std::vector) a lot, and iterates over that containers almost in every single function.

The iterating code looks like this:

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

But it produces the signed/unsigned mismatch warning (C4018 in Visual Studio).

Replacing int with some unsigned type is a problem because we frequently use OpenMP pragmas, and it requires the counter to be int.

I'm about to suppress the (hundreds of) warnings, but I'm afraid I've missed some elegant solution to the problem.

On iterators. I think iterators are great when applied in appropriate places. The code I'm working with will never change random-access containers into std::list or something (so iterating with int i is already container agnostic), and will always need the current index. And all the additional code you need to type (iterator itself and the index) just complicates matters and obfuscates the simplicity of the underlying code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

烟沫凡尘 2024-11-09 15:37:25

一切都在您的 things.size() 类型中。它不是 int,而是 size_t(它存在于 C++ 中,而不是 C),它等于某种“常见”无符号类型,即 unsigned int代码> x86_32。

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

写得像这样

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

甚至更快是正确的

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

It's all in your things.size() type. It isn't int, but size_t (it exists in C++, not in C) which equals to some "usual" unsigned type, i.e. unsigned int for x86_32.

Operator "less" (<) cannot be applied to two operands of different sign. There's just no such opcodes, and standard doesn't specify, whether compiler can make implicit sign conversion. So it just treats signed number as unsigned and emits that warning.

It would be correct to write it like

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

or even faster

for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }
心欲静而疯不止 2024-11-09 15:37:25

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

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

这将向编译器清楚地指定您的意图:它不会再用警告来打扰您。

Ideally, I would use a construct like this instead:

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

This a has the neat advantage that your code suddenly becomes container agnostic.

And regarding your problem, if some library you use requires you to use int where an unsigned int would better fit, their API is messy. Anyway, if you are sure that those int are always positive, you may just do:

int int_distance = static_cast<int>(distance);

Which will specify clearly your intent to the compiler: it won't bug you with warnings anymore.

云朵有点甜 2024-11-09 15:37:25

如果您不能/不会使用迭代器,并且如果您不能/不会使用 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超出了 int 的范围") 之类的内容。

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

If you can't/won't use iterators and if you can't/won't use std::size_t for the loop index, make a .size() to int conversion function that documents the assumption and does the conversion explicitly to silence the compiler warning.

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

Then you write your loops like this:

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

The instantiation of this function template will almost certainly be inlined. In debug builds, the assumption will be checked. In release builds, it won't be and the code will be as fast as if you called size() directly. Neither version will produce a compiler warning, and it's only a slight modification to the idiomatic loop.

If you want to catch assumption failures in the release version as well, you can replace the assertion with an if statement that throws something like std::out_of_range("container size exceeds range of int").

Note that this solves both the signed/unsigned comparison as well as the potential sizeof(int) != sizeof(Container::size_type) problem. You can leave all your warnings enabled and use them to catch real bugs in other parts of your code.

一直在等你来 2024-11-09 15:37:25

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

>,我们在 中添加了header,正是针对这种场景。

比较两个整数 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

C++20 has now std::cmp_less

In , we have the standard constexpr functions

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

added in the <utility> header, exactly for this kind of scenarios.

Compare the values of two integers t and u. Unlike builtin comparison operators, negative signed integers always compare less than (and not equal to) unsigned integers: the comparison is safe against lossy integer conversion.

That means, if (due to some wired reasons) one must use the i as integer, the loops, and needs to compare with the unsigned integer, that can be done:

#include <utility> // std::cmp_less

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

This also covers the case, if we mistakenly static_cast the -1 (i.e. int)to unsigned int. That means, the following will not give you an error:

static_assert(1u < -1);

But the usage of std::cmp_less will

static_assert(std::cmp_less(1u, -1)); // error
黎夕旧梦 2024-11-09 15:37:25

您可以使用:

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

You can use:

  1. size_t type, to remove warning messages
  2. iterators + distance (like are first hint)
  3. only iterators
  4. function object

For example:

// 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());
}
自控 2024-11-09 15:37:25

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

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

}

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

I can also propose following solution for C++11.

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

}

(C++ is not smart enough for auto p = 0, so I have to put p = 0U....)

神经大条 2024-11-09 15:37:25

我会给你一个更好的想法

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

decltype

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

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

I will give you a better idea

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

decltype is

Inspects the declared type of an entity or the type and value category
of an expression.

So, It deduces type of things.size() and i will be a type as same as things.size(). So,
i < things.size() will be executed without any warning

别想她 2024-11-09 15:37:25

我有类似的问题。使用 size_t 不起作用。我尝试了另一种对我有用的方法。 (如下)

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

I had a similar problem. Using size_t was not working. I tried the other one which worked for me. (as below)

for(int i = things.size()-1;i>=0;i--)
{
 //...
}
不及他 2024-11-09 15:37:25

我只会做

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

I would just do

int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
    cout << primeNumber[i] << ' ';
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文