Перечисления C ++ подписаны или не подписаны? - PullRequest
102 голосов
/ 01 октября 2008

Перечисления C ++ подписаны или не подписаны? Кроме того, безопасно ли проверять входные данные, проверяя, что это <= ваше максимальное значение и пропустить> = ваше минимальное значение (при условии, что вы начали с 0 и увеличили на 1)?

Ответы [ 9 ]

99 голосов
/ 01 октября 2008

Пошли к источнику. Вот что говорится в стандарте C ++ 03 (ISO / IEC 14882: 2003) в п. 7.2-5 (декларации перечисления):

Базовый тип перечисления является интегральным типом, который может представлять все значения перечислителя, определенные в Перечень. это определенный реализацией, какой интеграл тип используется в качестве базового типа для перечисления, за исключением того, что базовый тип не должен быть больше чем Int, если значение перечислитель не может вписаться в int или без знака int.

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

59 голосов
/ 01 октября 2008

Вы не должны полагаться на какое-либо конкретное представление. Прочитайте следующую ссылку . Кроме того, в стандарте говорится, что это определяется реализацией, какой целочисленный тип используется в качестве базового типа для перечисления, за исключением того, что он не должен быть больше, чем int, если только некоторое значение не может вписаться в int или в unsigned int.

Короче говоря: вы не можете полагаться на перечисление как подписанное или неподписанное.

21 голосов
/ 01 октября 2008

Вы не должны зависеть от того, подписаны они или нет. Если вы хотите сделать их явно подписанными или неподписанными, вы можете использовать следующее:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum
14 голосов
/ 02 октября 2008

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

В C ++ 0x будут добавлены строго типизированные перечисления , которые позволят вам указать тип перечисления, например:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Тем не менее, даже сейчас некоторую простую проверку можно выполнить, используя enum в качестве переменной или типа параметра, например:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.
5 голосов
/ 17 июля 2014

Даже некоторые старые ответы получили 44 отзыва, я склонен не соглашаться со всеми из них. Короче говоря, я не думаю, что мы должны заботиться о underlying type из перечисления.

Прежде всего, C ++ 03 Тип enum - это отдельный отдельный тип, не имеющий понятия знака. Так как из стандарта C ++ 03 dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Итак, когда мы говорим о знаке типа enum, скажем, сравнивая 2 операнда enum с помощью оператора <, мы на самом деле говорим о неявном преобразовании типа enum в некоторый целочисленный тип. Это знак этого интегрального типа, который имеет значение . И при преобразовании enum в целочисленный тип применяется это утверждение:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

И, по-видимому, базовый тип перечисления не имеет никакого отношения к Интегральному продвижению. Поскольку стандарт определяет интегральное продвижение следующим образом:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

Таким образом, становится ли тип перечисления signed int или unsigned int, зависит от того, может ли signed int содержать все значения определенных перечислителей, а не базовый тип перечисления.

см. Мой связанный вопрос Неверный знак типа перечисления C ++ после преобразования в интегральный тип

5 голосов
/ 01 октября 2008

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

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

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.
4 голосов
/ 03 октября 2008

В дополнение к тому, что другие уже сказали о подписанном / неподписанном, вот что стандарт говорит о диапазоне перечислимого типа:

7.2 (6): «Для перечисления, где e (min) - наименьший перечислитель, а e (max) - наибольший, значения перечисления являются значениями базового типа в диапазоне от b (min) до b (max), где b (min) и b (max) являются, соответственно, наименьшим и наибольшим значениями наименьшего битового поля, которое может хранить e (min) и e (max). Можно определить перечисление, которое имеет значения, не определенные ни одним из перечислителей. "

Так, например:

enum { A = 1, B = 4};

определяет перечислимый тип, где e (min) равно 1, а e (max) равно 4. Если базовый тип подписан как int, то наименьшее требуемое битовое поле имеет 4 бита, а если целые числа в вашей реализации являются дополнением до двух, то допустимый диапазон перечисления составляет от -8 до 7. Если базовый тип не подписан, то он имеет 3 бита, а диапазон - от 0 до 7. Если вам не безразлична документация компилятора (например, если вы хотите привести целые значения, отличные перечислителей для перечислимого типа, тогда вам нужно знать, находится ли значение в диапазоне перечисления или нет - если нет, результирующее значение перечисления не указано).

Являются ли эти значения действительными входными данными для вашей функции, может отличаться от того, являются ли они допустимыми значениями перечислимого типа. Ваш проверочный код, вероятно, беспокоится о первом, а не о втором, и поэтому в этом примере, по крайней мере, следует проверять> = A и <= B. </p>

4 голосов
/ 01 октября 2008

В будущем с C ++ 0x будут доступны строго типизированные перечисления , которые будут иметь несколько преимуществ (таких как безопасность типов, явные базовые типы или явная область видимости). С этим вы можете быть лучше уверены в знаке типа.

0 голосов

Проверьте это с помощью std::is_signed<std::underlying_type + перечислений с областью по умолчанию int

https://en.cppreference.com/w/cpp/language/enum подразумевает:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHub upstream .

Скомпилируйте и запустите:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Выход:

0

Протестировано на Ubuntu 16.04, GCC 6.4.0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...