Перечисления C ++ подписаны или неподписаны? Кроме того, безопасно ли проверять ввод, проверяя, что это <= ваше максимальное значение, и не учитывать> = ваше минимальное значение (при условии, что вы начали с 0 и увеличили на 1)?
Перечисления C ++ подписаны или неподписаны? Кроме того, безопасно ли проверять ввод, проверяя, что это <= ваше максимальное значение, и не учитывать> = ваше минимальное значение (при условии, что вы начали с 0 и увеличили на 1)?
Ответы:
Вы не должны полагаться на какое-либо конкретное представление. Прочтите следующую ссылку . Кроме того, в стандарте говорится, что это определяется реализацией, какой интегральный тип используется в качестве базового типа для перечисления, за исключением того, что он не должен быть больше, чем int, если какое-то значение не может поместиться в int или беззнаковое int.
Вкратце: вы не можете полагаться на то, что перечисление подписано или неподписано.
Пойдем к первоисточнику. Вот что говорится в документе стандарта C ++ 03 (ISO / IEC 14882: 2003) в 7.2-5 (Объявления перечисления):
Базовый тип перечисления - это интегральный тип, который может представлять все значения перечислителя, определенные в перечислении. Это определяется реализацией, какой интегральный тип используется в качестве базового типа для перечисления, за исключением того, что базовый тип не должен быть больше, чем 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
перечислении.
Во-первых, тип Enum в C ++ 03 - это отдельный тип, не имеющий понятия знака. Поскольку из стандарта C ++ 03dcl.enum
7.2 Enumeration declarations
5 Each enumeration defines a type that is different from all other types....
Поэтому, когда мы говорим о знаке типа перечисления, скажем, при сравнении двух операндов перечисления с использованием <
оператора, мы фактически говорим о неявном преобразовании типа перечисления в некоторый целочисленный тип. Важен только знак этого интегрального типа . И при преобразовании enum в целочисленный тип применяется следующее утверждение:
9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).
И, по-видимому, основной тип перечисления не имеет ничего общего с Integral Promotion. Поскольку стандарт определяет интегральное продвижение следующим образом:
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 ++ после преобразования в интегральный тип
-Wsign-conversion
. Мы используем его, чтобы выявлять непреднамеренные ошибки в нашем коде. Но +1 за ссылку на стандарте, и указывая на то , что перечисление не имеет типа ( по signed
сравнению unsigned
) , связанный с ним.
В будущем с 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. Если вас это интересует, проверьте документацию по компилятору (например, если вы хотите привести целые значения, отличные от перечислителей, к перечислимого типа, тогда вам нужно знать, находится ли значение в диапазоне перечисления или нет - если нет, результирующее значение перечисления не указано).
Вопрос о том, являются ли эти значения допустимыми входными данными для вашей функции, может отличаться от того, являются ли они допустимыми значениями перечисляемого типа. Ваш проверочный код, вероятно, беспокоится о первом, а не о втором, поэтому в этом примере следует, по крайней мере, проверять> = A и <= B.
Проверьте это с помощью std::is_signed<std::underlying_type
перечислений + scoped enums по умолчанию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>()));
}
Скомпилируйте и запустите:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
Вывод:
0
Проверено на Ubuntu 16.04, GCC 6.4.0.