Почему Qt использует знаковый целочисленный тип для своих контейнерных классов?
Вопрос ясен.
Интересно, почему они вообще думали, что это будет удобно, так как явно отрицательные экскременты непригодны для использования в контейнерах, которые будут использоваться с ними (см., например QList
's docs ).
Я думал, что они хотели разрешить это для какой-то сумасшедшей формы индексации, но это кажется неподдерживаемым?
Он также генерирует тонну (правильных) предупреждений компилятора о приведении и сравнении типов со знаком / без знака (на MSVC).
Это только кажется по какой-то причине несовместима с STL по дизайну...
2 ответа:
Потому что на самом деле вы обычно хотите выполнять арифметику по индексам, а это означает, что вы можете создать временные значения, которые являются отрицательными. Это явно болезненно, когда базовый тип индексирования не имеет знака.
Единственное подходящее время для использования беззнаковых чисел - это арифметика по модулю. Использование "unsgined" в качестве некоторого спецификатора контракта " число в диапазоне [0...- он просто неуклюж и слишком груб, чтобы быть полезным.
Рассмотрим: какой тип я должен использовать для представления идея о том, что число должно быть положительным целым числом от 1 до 10? Почему 0...2^x более специальный диапазон?
Хотя я глубоко сочувствую линии рассуждений Криса, я не соглашусь здесь (по крайней мере, частично, я играю адвоката дьявола). Нет ничего плохого в использовании неподписанных типов для размеров, и это может даже быть полезно в некоторых обстоятельствах.
Оправдание Криса для типов со знакомыми размерами заключается в том, что они естественным образом используются в качестве индексов массива, и вы можете сделать арифметику для индексов массива, и эта арифметика может создавать временные значения, которые являются отрицательными.
Это отлично, и беззнаковая арифметика не создает никаких проблем при этом, если вы убедитесь, что правильно интерпретируете свои значения при сравнении. Поскольку поведение переполнения целых чисел без знака полностью определено, временные переполнения в отрицательный диапазон (или в огромные положительные числа) не приводят к какой-либо ошибке, если они исправлены до выполнения сравнения.
Иногда, поведение переполнения даже желательно, как поведение переполнения unsigned арифметика делает определенные проверки диапазона выразимыми как одно сравнение, которое в противном случае потребовало бы двух сравнений. Если я хочу проверить, находится ли
x
в диапазоне[a,b]
и все значения без знака, я могу просто сделать:if (x - a < b - a) { }
Это не работает со знаковыми переменными; такие проверки диапазона довольно распространены с размерами и смещениями массива.
Я уже упоминал, что преимущество состоит в том, что арифметика переполнения имеет определенные результаты. Если индексная арифметика переполняет знаковый тип, то поведение определяется реализацией; нет никакого способа сделать вашу программу переносимой. Используйте беззнаковый тип, и эта проблема исчезнет. По общему признанию, это относится только к огромным смещениям, но это касается некоторых применений. В принципе, возражения против неподписанных типов часто преувеличены. Реальная проблема заключается в том, что большинство программистов на самом деле не задумываются о точной семантике кода, который они пишут, и для малых целочисленных значений знаковые типы ведут себя более близко к руководствуйтесь своей интуицией. Однако размеры данных растут довольно быстро. Когда мы имеем дело с буферами или базами данных, мы часто выходим за пределы диапазона "малых", и знаковое переполнение гораздо более проблематично правильно обрабатывать, чем беззнаковое переполнение. Решение заключается не в том, чтобы "не использовать неподписанные типы", а в том, чтобы "тщательно продумать код, который вы пишете, и убедиться, что вы его понимаете".