Являются ли перечисления C ++ подписанными или беззнаковыми?

StackOverflow https://stackoverflow.com/questions/159034

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

Давайте обратимся к источнику.Вот что говорится в документе стандарта C ++ 03 (ISO / IEC 14882: 2003) в 7.2-5 (Объявления перечислений):

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

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

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

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

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

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

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned 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.

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

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

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.

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

Во-первых, C ++ 03 Enum type - это отдельный тип, не имеющий понятия знака.Начиная со стандарта C ++ 03 dcl.enum

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

Итак, когда мы говорим о знаке перечисляемого типа, скажем, при сравнении 2-х перечисляемых операндов с использованием < оператор, на самом деле мы говорим о неявном преобразовании типа 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 ++ После Преобразования в Целочисленный тип

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

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

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.Проверьте документацию вашего компилятора, если вам интересно (например, если вы хотите привести целые значения, отличные от перечислителей, к перечисляемому типу, тогда вам нужно знать, находится ли значение в диапазоне перечисления или нет - если нет, то результирующее значение enum не указано).

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

Проверьте это с помощью 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.

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

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

Выходной сигнал:

0

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top