Подтвердить что ты не робот

Должен ли я использовать int или unsigned int при работе с контейнером STL?

Относится к этому руководству:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Integer_Types#Integer_Types

Google предлагает использовать int в течение большей части времени.
Я стараюсь следовать этому руководству, и единственная проблема связана с контейнерами STL.

Например:
1.

void SetElement(int Index, int Value)
{
    if (Index > Vector.size()) return;
    ...
}

Если я использую int для Index, здесь я получил предупреждение.

2.

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

Когда дело доходит до счетчика циклов, то такое же предупреждение.

Если я объявляю Index или i как unsigned int, тогда он распространяется, я должен объявить больше переменных как unsigned int и не будет никакой согласованности.

Лучший способ, который я могу думать, - использовать бросок как:
if (Index > static_cast<int>(Vector.size()) ... или
for (int i = 0; i < static_cast<int>(Vector.size()); ++i) ...
Но мне действительно не нравятся ролики.

Любые предлагаемые способы?

P.S.
Есть больше причин, чем пример для цикла. Чтобы использовать только целое число со знаком, я могу избежать предупреждений с подписью/без знака, отливок,
и убедитесь, что каждое значение может быть отрицательным (быть последовательным),
и я всегда могу использовать -1 для недопустимого значения.

Существует много случаев, когда он смешивает счетчик циклов с некоторым элементом константы или структуры и т.д.
Если signed/unsigned не является согласованным, тогда всегда будут предупреждения и отливки,
что раздражает и бессмысленно.
Поэтому я хочу объединить все целые числа, которые нужно подписать.

4b9b3361

Ответ 1

Неподписанные типы имеют три характеристики, один из которых качественно "хорош" и один из которых качественно "плох":

  • Они могут содержать в два раза больше значений, чем тип подписанного типа (хороший)
  • Версия size_t (то есть 32-разрядная на 32-разрядной машине, 64-разрядная на 64-разрядной машине и т.д.) полезна для представления памяти (адреса, размеры и т.д.) (нейтральная)
  • Они обходятся ниже 0, поэтому вычитание 1 в цикле или использование -1 для представления недопустимого индекса может привести к ошибкам (bad.) Подписанные типы wrap тоже.

STL использует неподписанные типы из-за первых двух пунктов выше: чтобы не ограничивать потенциальный размер подобных массиву классов, таких как vector и deque (хотя вы должны задать вопрос, как часто вам нужно 4294967296 элементы в структуре данных); потому что отрицательное значение никогда не будет действительным индексом для большинства структур данных; и потому что size_t - это правильный тип, используемый для представления чего-либо, связанного с памятью, такого как размер структуры и связанные с ней вещи, такие как длина строки (см. ниже). Это не обязательно хорошая причина для использования это для индексов или других целей без памяти, таких как переменная цикла. Причина, по которой это лучше всего делать на С++, выглядит как обратная конструкция, потому что то, что используется в контейнерах, а также другие методы, и когда-то использовало остальную часть кода, должно совпадать, чтобы избежать той же проблемы, с которой вы сталкиваетесь.

Вы должны использовать подписанный тип, когда значение может стать отрицательным.

Вы должны использовать неподписанный тип, когда значение не может стать отрицательным (возможно, это не так).

Вы должны использовать size_t при обработке размеров памяти (результат sizeof, часто такие вещи, как длина строк и т.д.). Он часто выбирается как используемый по умолчанию тип без знака, потому что он соответствует платформе, с которой компилируется код. Например, длина строки size_t, потому что в строке может быть только 0 или более элементов, и нет оснований ограничивать метод длины строки, произвольно меньший, чем то, что может быть представлено на платформе, например, 16 (0-65535) на 32-битной платформе. Примечание (спасибо комментатору Morwen) std::intptr_t или std::uintptr_t, которые концептуально похожи - всегда будут подходящего размера для вашей платформы - и должны использоваться для памяти адреса, если вы хотите что-то, что не указатель. Примечание 2 (спасибо комментатору rubenvb), что строка может содержать только теги size_t-1 из-за значения npos. Подробности ниже.

Это означает, что если вы используете -1 для представления недопустимого значения, вы должны использовать целые числа со знаком. Если вы используете цикл для итерации по сравнению с вашими данными, вам следует использовать целое число со знаком, если вы не уверены, что конструкция цикла верна (и, как указано в одном из других ответов, они легко ошибаются). IMO, вы должны не прибегать к трюкам, чтобы обеспечить работу кода - если код требует трюков, что часто является сигналом опасности. Кроме того, это будет сложнее понять для тех, кто следит за вами и читает ваш код. Оба эти причины не следует следовать @Jasmin Gray ответ выше.

итераторы

Однако использование целых циклов для итерации по содержимому структуры данных - это неправильный способ сделать это на С++, поэтому в некотором смысле аргумент над подписанными vs без знака для циклов является спорным. Вместо этого вы должны использовать итератор:

std::vector<foo> bar;
for (std::vector<foo>::const_iterator it = bar.begin(); it != bar.end(); ++it) {
  // Access using *it or it->, e.g.:
  const foo & a = *it;

Когда вы это сделаете, вам не нужно беспокоиться о приведениях, подписке и т.д.

Итераторы могут быть вперёд (как указано выше) или наоборот, для итерации назад. Используйте тот же синтаксис it != bar.end(), потому что end() сигнализирует о завершении итерации, а не о конце базового концептуального массива, дерева или другой структуры.

Другими словами, ответ на ваш вопрос "Должен ли я использовать int или unsigned int при работе с контейнерами STL?" "Ничего. Вместо этого используйте итераторы. Узнайте больше о:

Что осталось?

Если вы не используете целочисленный тип для циклов, что осталось? Ваши собственные значения, зависящие от ваших данных, но которые в вашем случае включают использование -1 для недопустимого значения. Это просто. Использовать подписанный. Просто будьте последовательны.

Я очень верю в использование естественных типов, таких как перечисления и целые числа со знаком. Они более точно соответствуют нашим концептуальным ожиданиям. Когда ваш ум и код выровнены, вы с меньшей вероятностью напишите багги-код и, скорее всего, выразительно напишите правильный, чистый код.

Ответ 2

Используйте тип, возвращаемый контейнером. В этом случае size_t - целочисленный тип без знака. (Чтобы быть техническим, это std::vector<MyType>::size_type, но обычно это определяется size_t, поэтому вы можете использовать size_t. unsigned тоже отлично)

Но в целом используйте правильный инструмент для правильной работы. Является ли "индекс" когда-либо предполагаемым отрицательным? Если нет, не делайте его подписанным.

Постепенно вам не нужно вводить "unsigned int". "unsigned" является сокращением для одного и того же типа переменной:

int myVar1;
unsigned myVar2;

На странице, связанной с исходным вопросом, говорилось:

Некоторые люди, в том числе некоторые авторы учебников, рекомендуют использовать unsigned типы для представления чисел, которые никогда не являются отрицательными. Это предназначено как форма самостоятельной документации. Однако в C преимущества таких документация перевешивается реальными ошибками, которые она может представить.

Это не просто самостоятельная документация, она использует правильный инструмент для правильной работы. Сказать, что "неподписанные переменные могут вызывать ошибки, поэтому не использовать неподписанные переменные" - это глупо. Подписанные переменные также могут вызывать ошибки. Таким образом, можно плавать (больше целых). Единственный гарантированный без ошибок код - это код, который не существует.

Их пример того, почему unsigned is evil, - это этот цикл:

for (unsigned int i = foo.Length()-1; i >= 0; --i)

У меня есть трудности с повторением цикла по циклу, и я обычно делаю ошибки (с подписанными или беззнаковыми целыми) с ним. Я вычитаю один из размера? Я делаю его больше-И-равным-0 или просто больше? Это небрежная ситуация для начала.

Итак, что вы делаете с кодом, который, как вы знаете, у вас проблемы? Вы меняете свой стиль кодирования, чтобы устранить проблему, упростить ее и упростить для чтения и облегчить ее запоминание. В петле есть ошибка. Ошибка: они хотели разрешить значение ниже нуля, но они решили сделать его неподписанным. Это их ошибка.

Но вот простой трюк, который облегчает чтение, запоминание, запись и запуск. С неподписанными переменными. Здесь разумная вещь (очевидно, это мое мнение).

for(unsigned i = myContainer.size(); i--> 0; )
{
    std::cout << myContainer[i] << std::endl;
}

Без знака. Он всегда работает. От начального размера ничего отрицательного. Не стоит беспокоиться о нижнем течении. Это просто работает. Это просто умно. Правильно, не прекращайте использовать неподписанные переменные, потому что кто-то где-то сказал, что они ошиблись в цикле for() и не смогли тренироваться, чтобы не совершить ошибку.

Трюк для запоминания:

  • Установите 'i' в размере. (не беспокойтесь о вычитании одного из них)
  • Сделать 'i' равным 0, как стрелка. i --> 0 (это комбинация пост-декрементирующего (i-) и большего, чем сравнение (i > 0))

Лучше научить себя трюкам правильно закодировать, а затем выбросить инструменты, потому что вы не правильно кодируете.

Что бы вы хотели видеть в своем коде?

for(unsigned i = myContainer.size()-1; i >= 0; --i)

Или:

for(unsigned i = myContainer.size(); i--> 0; )

Не потому, что это меньше символов для ввода (это было бы глупо), а потому, что это меньше умственного беспорядка. Это проще для умственного анализа, когда вы просматриваете код, и легче обнаруживать ошибки.


Попробуйте сам код