Я нашел это в коде, над которым сейчас работаю, и подумал, что это причина некоторых моих проблем.
Где-то в заголовке:
enum SpecificIndexes{
//snip
INVALID_INDEX = -1
};
Потом - инициализация:
nextIndex = INVALID_INDEX;
и использовать
if (nextIndex != INVALID_INDEX)
{
//do stuff
}
Отлаживая код, значения в nextIndex не совсем имели смысла (они были очень большими), и я обнаружил, что он был объявлен:
unsigned int nextIndex;
Итак, первоначальная установка INVALID_INDEX была ниже беззнакового int и установила для него огромное число. Я предположил, что это было причиной проблемы, но если присмотреться, тест
if (nextIndex != INVALID_INDEX)
Вёл себя правильно, то есть никогда не выполнял тело if, когда nextIndex был «большим + ve значение».
Это верно? Как это происходит? Приводится ли значение перечисления неявно к беззнаковому int того же типа, что и переменная, и, следовательно, обертывается таким же образом?





Фактически, -1 неявно приводится к эквивалентному ему беззнаковому значению, когда ему присваивается значение nextValue. Эквивалентное значение без знака - это значение с таким же побитовым представлением (это 111111111111 ..., это максимальное значение без знака).
Позже, в операторе сравнения, происходит еще одно неявное приведение.
Так что сейчас это работает, но может вызвать проблемы в будущем. Никогда не рекомендуется смешивать значения со знаком и без знака.
А как насчет теста со значением enum, я предполагаю, что это тоже неявное приведение?
Для протокола, я согласился с точкой зрения xan и соответствующим образом отредактировал свою запись. Но я не знаю, почему пропал мой комментарий.
«Эквивалент без знака - это значение с таким же побитовым представлением» - это верно, если реализация использует представление с дополнением до двух для целочисленных типов со знаком. Однако это не определение и неверно для представлений с дополнением до единицы или знакового значения. Которые в любом случае редки.
Да всему. Это действительный код, это также часто используемый код C++ на стороне библиотеки, тем более что в современном C++ (это странно, когда вы видите его в первый раз, но на самом деле это очень распространенный образец).
Затем перечисления являются знаковыми целыми числами, но они неявно преобразуются в целые числа без знака, теперь это в зависимости от вашего компилятора может выдавать предупреждение, но оно все еще очень часто используется, однако вы должны явно привести его, чтобы это было ясно для сопровождающих.
Я категорически не согласен. Во-первых, ваш код говорит, что nextIndex беззнаковый, хотя на самом деле это не так, вводя читателя в заблуждение. Во-вторых, если позже вы решите добавить новые положительные значения в SpecificIndexes и использовать для проверок операторы> или <, ничего не будет работать.
перечисления подписаны, но вы правы насчет ошибок операторов> <, например, если вы сортируете их в контейнере. Однако хорошая библиотека могла бы безопасно обработать этот случай или вообще не позволить вам добавить invalidIndex в контейнер. В любом случае, вы увидите такие вещи даже в Boost ...
Да, я нахожу эту проблему. Функции, использующие nextIndex, утверждают, что он> 0, поэтому изменение его для использования подписанного int - нетривиальный вариант, а другой код, я думаю, зависит от того, как он был реализован. Это довольно сбивает с толку.
для меня пахнет временем рефакторинга :) В любом случае быстрое решение - Assert (index> 0 && index! = INVALID_INDEX); Это, по крайней мере, правильно и делает утверждение лучше документированным.
Да, я считаю, что перечисления подписаны. Изменять
unsigned int nextIndex;
к
int nextIndex;
и ваша программа должна работать.
Он работает как есть (это часть существующей системы, а не то, что я пишу с нуля) - мой вопрос был Почему, он работал так же, как и выглядел очень неправильно!
перечисления могут быть представлены знаковыми или беззнаковыми целочисленными типами в зависимости от того, содержат ли они какие-либо отрицательные значения и как себя чувствует компилятор. Пример здесь содержит отрицательное значение и, следовательно, должен быть представлен целочисленным типом со знаком.
Сравнение равенства между знаковыми и беззнаковыми типами безопасно и обычно делает то, что задумал автор - знаковое значение сначала будет преобразовано в беззнаковое, а результат этого определяется стандартом C++ и интуитивно понятен (по крайней мере, когда вы знать тип назначения. За исключением случаев, когда целые числа не являются дополнением до двух. Возможно, это не так интуитивно понятно, но обычно не вызывает проблем).
Сравнение заказов с большей вероятностью приведет к ошибкам. Например:
SpecificIndexes value = INVALID_VALUE;
return (value >= 0);
возвращает false, но:
unsigned int value = INVALID_VALUE;
return (value >= 0);
возвращает истину. Иногда автор не оценивает разницу, особенно если тип «значения» не так очевиден в момент использования. Однако компилятор вполне может предупредить о втором примере, потому что (value> = 0) - это тавтология.
Стандарт C++ позволяет реализации использовать подписанный тип для перечислений, но не требует этого. Следовательно, вы не можете в целом предположить, что можно безопасно помещать отрицательные числа в перечисление.
ИТИМ «Потом позже - переуступка». Инициализация будет
unsigned nextIndex = INVALID_INDEX