С ++ перечисление для сравнения беззнакового int

Я нашел это в коде, над которым сейчас работаю, и подумал, что это причина некоторых моих проблем.

Где-то в заголовке:

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 того же типа, что и переменная, и, следовательно, обертывается таким же образом?

ИТИМ «Потом позже - переуступка». Инициализация будет unsigned nextIndex = INVALID_INDEX

MSalters 20.11.2008 15:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
21 200
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Фактически, -1 неявно приводится к эквивалентному ему беззнаковому значению, когда ему присваивается значение nextValue. Эквивалентное значение без знака - это значение с таким же побитовым представлением (это 111111111111 ..., это максимальное значение без знака).

Позже, в операторе сравнения, происходит еще одно неявное приведение.

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

А как насчет теста со значением enum, я предполагаю, что это тоже неявное приведение?

xan 19.11.2008 14:36

Для протокола, я согласился с точкой зрения xan и соответствующим образом отредактировал свою запись. Но я не знаю, почему пропал мой комментарий.

Gorpik 19.11.2008 17:40

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

Steve Jessop 21.11.2008 16:13
Ответ принят как подходящий

Да всему. Это действительный код, это также часто используемый код C++ на стороне библиотеки, тем более что в современном C++ (это странно, когда вы видите его в первый раз, но на самом деле это очень распространенный образец).

Затем перечисления являются знаковыми целыми числами, но они неявно преобразуются в целые числа без знака, теперь это в зависимости от вашего компилятора может выдавать предупреждение, но оно все еще очень часто используется, однако вы должны явно привести его, чтобы это было ясно для сопровождающих.

Я категорически не согласен. Во-первых, ваш код говорит, что nextIndex беззнаковый, хотя на самом деле это не так, вводя читателя в заблуждение. Во-вторых, если позже вы решите добавить новые положительные значения в SpecificIndexes и использовать для проверок операторы> или <, ничего не будет работать.

Gorpik 19.11.2008 14:50

перечисления подписаны, но вы правы насчет ошибок операторов> <, например, если вы сортируете их в контейнере. Однако хорошая библиотека могла бы безопасно обработать этот случай или вообще не позволить вам добавить invalidIndex в контейнер. В любом случае, вы увидите такие вещи даже в Boost ...

Robert Gould 19.11.2008 15:05

Да, я нахожу эту проблему. Функции, использующие nextIndex, утверждают, что он> 0, поэтому изменение его для использования подписанного int - нетривиальный вариант, а другой код, я думаю, зависит от того, как он был реализован. Это довольно сбивает с толку.

xan 19.11.2008 15:06

для меня пахнет временем рефакторинга :) В любом случае быстрое решение - Assert (index> 0 && index! = INVALID_INDEX); Это, по крайней мере, правильно и делает утверждение лучше документированным.

Robert Gould 19.11.2008 15:15

Да, я считаю, что перечисления подписаны. Изменять

unsigned int nextIndex;

к

int nextIndex;

и ваша программа должна работать.

Он работает как есть (это часть существующей системы, а не то, что я пишу с нуля) - мой вопрос был Почему, он работал так же, как и выглядел очень неправильно!

xan 19.11.2008 15:04

перечисления могут быть представлены знаковыми или беззнаковыми целочисленными типами в зависимости от того, содержат ли они какие-либо отрицательные значения и как себя чувствует компилятор. Пример здесь содержит отрицательное значение и, следовательно, должен быть представлен целочисленным типом со знаком.

Сравнение равенства между знаковыми и беззнаковыми типами безопасно и обычно делает то, что задумал автор - знаковое значение сначала будет преобразовано в беззнаковое, а результат этого определяется стандартом C++ и интуитивно понятен (по крайней мере, когда вы знать тип назначения. За исключением случаев, когда целые числа не являются дополнением до двух. Возможно, это не так интуитивно понятно, но обычно не вызывает проблем).

Сравнение заказов с большей вероятностью приведет к ошибкам. Например:

SpecificIndexes value = INVALID_VALUE;
return (value >= 0);

возвращает false, но:

unsigned int value = INVALID_VALUE;
return (value >= 0);

возвращает истину. Иногда автор не оценивает разницу, особенно если тип «значения» не так очевиден в момент использования. Однако компилятор вполне может предупредить о втором примере, потому что (value> = 0) - это тавтология.

Стандарт C++ позволяет реализации использовать подписанный тип для перечислений, но не требует этого. Следовательно, вы не можете в целом предположить, что можно безопасно помещать отрицательные числа в перечисление.

Другие вопросы по теме