Как я могу справиться с предупреждениями "подписанный/неподписанный несоответствие" (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] << ' ';