Как я могу справиться с предупреждениями "подписанный/неподписанный несоответствие" (C4018)?

Я работаю с большим количеством кода вычисления, написанного на С++, с высокой производительностью и низким объемом памяти. Он много использует контейнеры STL (в основном vector) и выполняет итерации по этим контейнерам почти в каждой отдельной функции.

Итерирующий код выглядит следующим образом:

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

но он выдает предупреждение о несоответствии с подписью/без знака (C4018 в Visual Studio).

Замена int на некоторый тип unsigned является проблемой, потому что мы часто используем прагмы OpenMP, и для этого требуется, чтобы счетчик был int.

Я собираюсь подавить (сотни) предупреждений, но, боюсь, я пропустил какое-то изящное решение проблемы.

В итераторах. Я думаю, что итераторы великолепны, когда применяются в соответствующих местах. Код, с которым я работаю, никогда не изменит контейнеры с произвольным доступом в list или что-то (так что итерация с помощью int i уже не является агентом), и всегда будет нужен текущий индекс. И весь дополнительный код, который вам нужно ввести (сам итератор и индекс), просто усложняет вопросы и затуманивает простоту базового кода.

Ответ 1

Все это в вашем типе things.size(). Это не int, но size_t (он существует в С++, а не в C), который равен некоторому "обычному" неподписанному типу, т.е. unsigned int для x86_32.

Оператор "less" (<) не может применяться к двум операндам разного знака. Там просто нет таких кодов операций, и стандарт не указывает, может ли компилятор сделать неявное преобразование знака. Таким образом, он просто обрабатывает подписанный номер как unsigned и испускает это предупреждение.

Правильно было бы написать его как

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

или даже быстрее

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

Ответ 2

В идеале я бы использовал вместо этого конструкцию:

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

Что конкретно укажет ваше намерение на компилятор: оно больше не будет предупреждать вас предупреждениями.

Ответ 3

Если вы не можете/не будете использовать итераторы, и если вы не можете/не будете использовать std::size_t для индекса цикла, сделайте функцию преобразования .size() to 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). Вы можете оставить все свои предупреждения включенными и использовать их, чтобы поймать настоящие ошибки в других частях вашего кода.

Ответ 4

Вы можете использовать:

  • size_t, чтобы удалить предупреждающие сообщения
  • итераторы + расстояние (например, первые подсказки)
  • только итераторы
  • объект функции

Например:

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

Ответ 5

Я дам вам лучшую идею

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

decltype является

Проверяет объявленный тип объекта или тип и категорию выражения.

Итак, он выводит тип things.size(), а i будет таким же, как things.size(). Так, i < things.size() будет выполняться без предупреждения

Ответ 6

У меня была аналогичная проблема. Использование size_t не работает. Я попробовал другой, который работал на меня. (как показано ниже)

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

Ответ 7

Я также могу предложить следующее решение для С++ 11.

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

}

(С++ недостаточно умен для автоматического p = 0, поэтому мне нужно поставить p = 0U....)

Ответ 8

Я бы просто сделал

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