Используйте знаковые типы для арифметики
Причина
Большинство арифметических операций предполагается знаковыми; x - y даёт отрицательное число, когда y > x, за исключением редких случаев, когда вам действительно нужна модульная арифметика.
Пример
Беззнаковая арифметика может давать неожиданные результаты, если вы этого не ожидаете. Это ещё более актуально при смешении знаковой и беззнаковой арифметики.
template<typename T, typename T2>
T subtract(T x, T2 y)
{
return x - y;
}
void test()
{
int s = 5;
unsigned int us = 5;
cout << subtract(s, 7) << '\n'; // -2
cout << subtract(us, 7u) << '\n'; // 4294967294
cout << subtract(s, 7u) << '\n'; // -2
cout << subtract(us, 7) << '\n'; // 4294967294
cout << subtract(s, us + 2) << '\n'; // -2
cout << subtract(us, s + 2) << '\n'; // 4294967294
}
Здесь мы явно показываем, что происходит, но если бы вы увидели us - (s + 2) или s += 2; ...; us - s, могли бы вы уверенно предположить, что результат будет напечатан как 4294967294?
Исключение
Используйте беззнаковые типы, если вам действительно нужна модульная арифметика — добавьте комментарии, поясняющие зависимость от поведения при переполнении, поскольку такой код будет удивительным для многих программистов.
Пример
Стандартная библиотека использует беззнаковые типы для индексов. Встроенные массивы используют знаковые типы для индексов. Это делает неожиданности (и ошибки) неизбежными.
int a[10];
for (int i = 0; i < 10; ++i) a[i] = i;
vector<int> v(10);
// сравнение знакового с беззнаковым; некоторые компиляторы предупреждают, но мы не должны
for (gsl::index i = 0; i < v.size(); ++i) v[i] = i;
int a2[-2]; // ошибка: отрицательный размер
// OK, но количество int-ов (4294967294) настолько велико, что мы должны получить исключение
vector<int> v2(-2);
Используйте gsl::index для индексов; смотрите ES.107.
Контроль
- Отмечать смешанную знаковую и беззнаковую арифметику
- Отмечать результаты беззнаковой арифметики, присвоенные или выведенные как знаковые.
- Отмечать отрицательные литералы (например,
-2), используемые как индексы контейнеров. - (Во избежание лишних предупреждений) Не отмечать смешанные знаковые/беззнаковые сравнения, где один из аргументов —
sizeofили вызов.size()контейнера, а другой —ptrdiff_t.