Чи підписані або непідписані C ++ перерахунки? А внаслідок розширення чи безпечно перевірити вхід, перевіривши, що він <= ваше максимальне значення, і залиште> = ваше мінімальне значення (якщо припустити, що ви почали з 0 і збільшили на 1)?
Чи підписані або непідписані C ++ перерахунки? А внаслідок розширення чи безпечно перевірити вхід, перевіривши, що він <= ваше максимальне значення, і залиште> = ваше мінімальне значення (якщо припустити, що ви почали з 0 і збільшили на 1)?
Відповіді:
Не слід покладатися на якесь конкретне представництво. Прочитайте наступне посилання . Також стандарт говорить, що визначено реалізацією, який інтегральний тип використовується як базовий тип для enum, за винятком того, що він не повинен бути більшим за int, якщо якесь значення не може вміститися в int або неподписаний int.
Коротше кажучи: ви не можете розраховувати на те, що enum буде підписаний або непідписаний.
Перейдемо до джерела. Ось що йдеться у документі стандарту C ++ 03 (ISO / IEC 14882: 2003) у 7.2-5 (декларації перерахування):
Основний тип перерахунку - це інтегральний тип, який може представляти всі значення перерахунку, визначені в перерахунку. Визначено реалізацією, який інтегральний тип використовується як базовий тип для перерахування, за винятком того, що базовий тип не повинен бути більшим за int, якщо значення перелічувача не може вміститися в int або неподписаний 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 як змінну або тип параметра, як це:
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 як змінного типу. Наприклад:
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 - це окремий тип, який не має поняття знака. Оскільки від 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.
Отже, чи стає тип enum signed int
чи unsigned int
залежить від того, чи signed int
можуть містити всі значення визначених нумераторів, а не базовий тип enum.
Дивіться моє відповідне запитання Знак типу C ++ Enum Неправильний після переходу в інтегральний тип
-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 біти, а якщо ints у вашій реалізації є доповненням двох, то допустимий діапазон перерахунок становить від -8 до 7. Якщо базовий тип не підписаний, то він має 3 біти, а діапазон від 0 до 7. Перевірте свою документацію на компілятор, якщо вам це важливо (наприклад, якщо ви хочете передати цілісні значення, відмінні від нумераторів, до перелічений тип, тоді вам потрібно знати, чи знаходиться значення в діапазоні перерахування, чи ні - якщо не отримане значення перерахунку не визначене).
Чи є ці значення дійсними вхідними для вашої функції, може бути іншим питанням, чи не є вони дійсними значеннями перерахованого типу. Ваш перевіряючий код, ймовірно, хвилює перше, а не останнє, і тому в цьому прикладі слід хоча б перевіряти> = 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>()));
}
Складіть і запустіть:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
Вихід:
0
Тестовано на Ubuntu 16.04, GCC 6.4.0.