Enum class c что это
Перейти к содержимому

Enum class c что это

  • автор:

Перечисления (C++)

Перечисление — это определяемый пользователем тип, состоящий из набора именованных целочисленных констант, которые называются перечислителями.

В этой статье рассматриваются тип языка enum C++ стандарта ISO и тип область (или строго типизированный), enum class который представлен в C++11. Сведения о public enum class типах private enum class или типах в C++/CLI и C++/CX см. в статьях enum class (C++/CLI и C++/CX).

Синтаксис

enum-specifier :
enum-head enumerator-list необ. >
enum-head enumerator-list , >

opaque-enum-declaration :
enum-key attribute-specifier-seq opt enum-head-name enum-base opt ;

enum-key :
enum
enum class
enum struct

enum-base :
: type-specifier-seq

enumerator-list :
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition :
enumerator
enumerator = constant-expression

enumerator :
identifier attribute-specifier-seq необ.

Использование

// unscoped enum: // enum [identifier] [: type] ; // scoped enum: // enum [class|struct] [identifier] [: type] ; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum must have type specified enum class B; // scoped enum defaults to int but . enum class C : short; // . may have any integral underlying type 

Параметры

identifier
Имя типа, присваиваемое перечислению.

type
Базовый тип перечислителей; все перечислители имеют один базовый тип. Может быть любым целочисленным типом.

enum-list
Разделенный запятыми список перечислителей в перечислении. Каждый перечислитель или имя переменной в области должны быть уникальными. Однако значения могут повторяться. В перечислении un область d, область является окружающим область; в область перечислении, область сам по enum-list себе. В перечислении область может быть пустой список, который фактически определяет новый целочисленный тип.

class
Используя этот ключевое слово в объявлении, вы указываете, что перечисление область и identifier необходимо указать. Вы также можете использовать struct ключевое слово вместо class них, так как они семантически эквивалентны в этом контексте.

Перечисление область

Перечисление предоставляет контекст для описания диапазона значений, представленных как именованные константы. Эти именованные константы также называются перечислителями. В исходных типах C и C++ enum перечислители неквалифицированных отображаются на протяжении область, в которой объявлен. enum В область d перечисления имя перечислителя должно быть квалифицировано по enum имени типа. В следующем примере демонстрируется основное различие между двумя видами перечислений.

namespace CardGame_Scoped < enum class Suit < Diamonds, Hearts, Clubs, Spades >; void PlayCard(Suit suit) < if (suit == Suit::Clubs) // Enumerator must be qualified by enum type < /*. */>> > namespace CardGame_NonScoped < enum Suit < Diamonds, Hearts, Clubs, Spades >; void PlayCard(Suit suit) < if (suit == Clubs) // Enumerator is visible without qualification < /*. */ >> > 

Каждому имени в перечислении присваивается целочисленное значение, которое соответствует определенному месту в порядке значений в перечислении. По умолчанию первому значению присваивается 0, следующему — 1 и т. д., но можно задать значение перечислителя явным образом, как показано ниже:

enum Suit < Diamonds = 1, Hearts, Clubs, Spades >; 

Перечислителю Diamonds присваивается значение 1 . Последующие перечислители, если они не имеют явного значения, получают значение предыдущего перечислителя плюс один. В предыдущем примере Hearts имел бы значение 2, Clubs — значение 3 и т.д.

Каждый перечислитель обрабатывается как константа и должен иметь уникальное имя в область где enum определен (для не область d перечислений) или внутри enum себя (для перечислений область d). Значения, заданные именам, не должны быть уникальными. Например, рассмотрим это объявление перечисления Suit un область d:

enum Suit < Diamonds = 5, Hearts, Clubs = 4, Spades >; 

Значения Diamonds , Hearts Clubs и Spades имеют значение 5, 6, 4 и 5 соответственно. Обратите внимание, что 5 используется более одного раза; Это разрешено, даже если оно не может быть предназначено. Такие же правила распространяются на ограниченные перечисления.

Приведение правил

Константы перечисления un область d могут быть неявно int преобразованы в , но int никогда неявно преобразуется в значение перечисления. В следующем примере показано, что происходит при попытке назначить hand значение, которое не Suit является:

int account_num = 135692; Suit hand; hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit' 

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

int account_num = Hearts; //OK if Hearts is in an unscoped enum 

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

namespace ScopedEnumConversions < enum class Suit < Diamonds, Hearts, Clubs, Spades >; void AttemptConversions() < Suit hand; hand = Clubs; // error C2065: 'Clubs' : undeclared identifier hand = Suit::Clubs; //Correct. int account_num = 135692; hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit' hand = static_cast(account_num); // OK, but probably a bug. account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int' account_num = static_cast(Suit::Hearts); // OK > > 

Обратите внимание, что в строке hand = account_num; по-прежнему содержится ошибка, которая происходит при использовании неограниченных перечислений, как показано выше. Допускается явное приведение. Однако при использовании ограниченных перечислений попытка преобразования в следующем операторе — account_num = Suit::Hearts; — больше не будет разрешена без явного приведения.

Перечисления без перечислителей

Visual Studio 2017 версии 15.3 и более поздних версий (доступных и /std:c++17 более поздних версий): определяя перечисление (обычный или область d) с явным базовым типом и без перечислителей, вы можете в действительности ввести новый целочисленный тип, который не имеет неявного преобразования в любой другой тип. Используя этот тип вместо встроенного базового типа, можно исключить вероятность тонких ошибок, вызванных непреднамеренно неявными преобразованиями.

enum class byte : unsigned char < >; 

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

enum class byte : unsigned char < >; enum class E : int < >; E e1< 0 >; E e2 = E< 0 >; struct X < E e< 0 >; X() : e < 0 > < >>; E* p = new E< 0 >; void f(E e) <>; int main() < f(E< 0 >); byte i< 42 >; byte j = byte< 42 >; // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char' return 0; > 

enum class (C++/CLI и C++/CX)

Объявляет перечисление в области видимости пространства имен, которое является определяемым пользователем типом, состоящим из ряда именованных констант, называемых перечислителями.

Все среды выполнения

Замечания

C++/CX и C++/CLI поддерживают открытый класс перечисления и закрытый класс перечисления, которые схожи со стандартным классом перечисления C++, но с добавлением спецификатора доступа. В /CLR тип класса перечисления C++11 разрешен, но будет создавать предупреждение C4472, предназначенное для того, чтобы убедиться, что вам действительно требуется тип перечисления стандарта ISO, а не типы C++/CX или C++/CLI. Дополнительные сведения о стандарте ISO Standard C++ enum ключевое слово см. в перечислениях .

Среда выполнения Windows

Синтаксис

 access enum class enumeration-identifier [:underlying-type] < enumerator-list >[var]; accessenum structenumeration-identifier[:underlying-type] < enumerator-list >[var]; 

Параметры

access
Уровень доступа перечисления, который может иметь значение public или private .

enumeration-identifier
Имя перечисления.

underlying-type
(Необязательно) Базовый тип перечисления.

(Необязательно. только среда выполнения Windows) Базовый тип перечисления, который может быть bool , int16 char16 char , , , uint16 , int , uint32 int64 или . uint64

enumerator-list
Разделенный запятыми список имен перечислителей.

Значение каждого перечислителя — константное выражение, которое или определяется неявно компилятором, или явно нотацией enumerator = constant-expression. По умолчанию значение первого перечислителя ноль, если он определен неявно. Значение каждого следующего неявно определенного перечислителя — значение предыдущего перечислителя + 1.

var
(Необязательно) Имя переменной типа перечисления.

Замечания

Дополнительные сведения и примеры см. в разделе Перечисления.

Обратите внимание, что компилятор выводит сообщения об ошибках, если константное выражение, задающее значение перечислителя, не может быть представлено underlying-type. Однако компилятор не сообщает об ошибке для значения, недопустимого для базового типа. Например:

  • Если базовый тип является числовым, а перечислитель задает максимальное значение этого типа , значение следующего неявно определенного перечисления невозможно представить.
  • Если underlying-type является bool и более двух перечислителей определены неявно, то нельзя представить перечислители после первых двух.
  • Если underlying-type является char16 и значение перечисления в диапазоне от 0xD800 до 0xDFFF, то значение можно представить. Однако логически значение неверно, так как оно представляет половину пары символов-заместителей Юникода и не должно отображаться в изоляции.

Требования

Параметр компилятора: /ZW

Среда CLR

Синтаксис

 access enum class name [:type] < enumerator-list >var; accessenum structname [:type] < enumerator-list >var; 

Параметры

access
Уровень доступа перечисления. Может быть либо public , либо private .

enumerator-list
Разделенный запятыми список идентификаторов (перечислителей) в перечислении.

имя
Имя перечисления. Анонимные управляемые перечисления не допускаются.

type
(Необязательно) Базовый тип identifiers. Это может быть любой скалярный тип, например подписанные или неподписанные int версии , short или long . bool или char также допустимы.

var
(Необязательно) Имя переменной типа перечисления.

Замечания

enum class и enum struct являются эквивалентными объявлениями.

Существуют два типа перечислений: управляемые (C++/CX) и стандартные.

Управляемые перечисления (или перечисления C++/CX) могут быть определены следующим образом:

public enum class day ; 

Это семантически эквивалентно:

ref class day < public: static const int sun = 0; static const int mon = 1; >; 

Стандартное перечисление может быть определено следующим образом:

enum day2 < sun, mon >; 

Это семантически эквивалентно:

static const int sun = 0; static const int mon = 1; 

Управляемые имена перечислителей (identifiers) не вводятся в область, в которой определяется перечисление; все ссылки на перечислители должны быть полными (имя :: идентификатор). По этой причине нельзя определить анонимное управляемое перечисление.

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

В Visual Studio 2002 и Visual Studio 2003 перечислители были нестрого вводимыми (видимыми во внешней области, если не было другого идентификатора с таким же именем).

Если определен стандартный перечисление C++ (без class или struct ), компиляция с /clr помощью этого перечисления приведет к компиляции в виде управляемой перечисления. Перечисление по-прежнему имеет семантику неуправляемого перечисления. Обратите внимание, что компилятор вводит атрибут Microsoft::VisualC::NativeEnumAttribute для определения намерения программиста сделать перечисление собственным. Другие компиляторы просто увидят стандартное перечисление как управляемое перечисление.

Именованное стандартное перечисление, скомпилированное с параметром /clr , будет видимо в сборке как управляемое перечисление и может использоваться любым другим управляемым компилятором. Однако безымянное стандартное перечисление не будет видимо из сборки.

В Visual Studio 2002 и Visual Studio 2003 стандартное перечисление используется в качестве типа в параметре функции.

// mcppv2_enum.cpp // compile with: /clr enum E < a, b >; void f(E) int main()

Этот код выдал бы в MSIL для сигнатуры функции следующее:

void f(int32); 

Однако в текущих версиях компилятора стандартное перечисление выдается как управляемое перечисление с [NativeEnumAttribute], а в MSIL для сигнатуры функции выдается следующее:

void f(E) 

Дополнительные сведения о неуправляемых перечислителях см. в разделе Объявление перечислений C++.

Дополнительные сведения о перечислителях CLR см. в следующем разделе:

Требования

Параметр компилятора: /clr

Примеры

// mcppv2_enum_2.cpp // compile with: /clr // managed enum public enum class m < a, b >; // standard enum public enum n < c, d >; // unnamed, standard enum public enum < e, f >o; int main() < // consume managed enum m mym = m::b; System::Console::WriteLine("no automatic conversion to int: ", mym); System::Console::WriteLine("convert to int: ", (int)mym); // consume standard enum n myn = d; System::Console::WriteLine(myn); // consume standard, unnamed enum o = f; System::Console::WriteLine(o); > 
no automatic conversion to int: b convert to int: 1 1 1 

Enum class c что это

Перечисления (enum) представляют еще один способ определения своих типов. Их отличительной особенностью является то, что они содержат набор числовых констант. Перечисление имеет следующую форму:

enum class имя_перечисления < константа_1, константа_2, . константа_N>;

После ключевых enum class идет название перечисления, и затем в фигруных скобках перечисляются через запятую константы перечисления.

Определим простейшее перечисление:

enum class Day

В данном случае перечисление называется Day и представляет дни недели. В фигурных скобках заключены все дни недели. Фактически они представляют числовые константы.

Каждой константе сопоставляется некоторое числовое значение. По умолчанию первая константа получает в качестве значения 0, а остальные увеличиваются на единицу. Так, в примере выше Monday будет иметь значение 0, Tuesday — 1 и так далее. Таким образом, последняя константа — Sunday будет равна 6.

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

Day today ; // или так //Day today = Day::Thursday;

В данном случае определяется переменная today , которая равна Day::Thursday , то есть четвертой константе перечисления Day.

Чтобы вывести значение переменной на консоль, можно использовать преобразование к типу целочисленному типу:

#include enum class Day ; int main() < Day today ; std::cout (today)

То есть в данном случае на консоль будет выведено Today: 3 , так как константа Thursday имеет значение 3.

Мы также можем управлять установкой значений в перечислении. Так, мы можем задать начальное значение для одной контанты, тогда у последуюших констант значение увеличивается на единицу:

enum class Day ;

В данном случае Tuesday будет равно 2, а Sunday — 7.

Можно назначить каждой константе индивидуальное значение или сочетать этот подход с автоустановкой:

enum class Day ;

В данном случае Saturday будет равно 7, а Sunday — 1.

Можно даже назначать двум константам одно и то же значение:

enum class Day ;

Здесь константы Monday и Mon имеют одно и то же значение.

Можно присвоить константам значение уже имеющихся констант:

enum class Day ;

Стоит учитывать, что константы перечисления должны представлять целочисленные константы. Однако мы можем выбрать другой целочисленный тип, например, char :

enum class Operation: char ;

Если мы захотим вывести значения этих констант на консоль в виде символов, то необходимо преобразовать их к типу char :

#include enum class Operation: char ; int main() < std::cout (Operation::Add) (Operation::Subtract) (Operation::Multiply)

Применение перечислений

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

#include enum class Operation ; void calculate(int n1, int n2, Operation op) < switch (op) < case Operation::Add: std::cout > int main() < calculate(10, 6, Operation::Add); // 16 calculate(10, 6, Operation::Subtract); // 4 calculate(10, 6, Operation::Multiply); // 60 >

В данном случае все арифметические операции хранятся в перечислении Operation. В функции calculate зависимости от значения третьего параметра — применяемой операции выполняются определенные действия с двумя первыми параметрами.

Подключение констант перечисления

При обращении к контантам перечисления по умолчанию необходимо указывать название перечисления, например, Day::Monday . Но начиная со стандарта C++20 мы можем подключить константы перечисления в текущий контекст с помощью оператора using .

using enum Day;

И в дальнейшем использовать только имя констант:

#include enum class Day ; using enum Day; // подключаем константы перечисления в текущую область видимости int main() < Day today ; // используем только имя константы // или так //Day today = Thursday; std::cout (today) << std::endl; // 3 // выводим значение констаты Sunday std::cout (Sunday) 

Также мы можем подключить только одну константу:

using Day::Monday; // подключаем только Monday . Day today ;

В данном случае подключаем только константу Day::Monday . Для обращения к дргуим константам по прежднему необходимо использовать имя перечисления.

Поскольку такая возможность добавлена лишь начиная со стандарта С++20, то при компиляции с g++ или clang++ добавляется соответствующий флаг - -std=c++20

Перечисления в С-стиле

Стоит отметить, что раньше в С++ использовалась другая форма перечислений, которые пришли из языка С и определяются без ключевого слова class :

#include enum Day ; int main() < Day today = Tuesday; std::cout 

Такие перечисления еще называют unscoped (то есть не ограниченные ни какой областью видимостью). Естественно такие перечисления можно встретить в старых программах. Однако в виду того, что они потенциально могут привести к большему количеству ошибок, то в настоящее время такая форма все меньше и меньше используется.

Зачем нужен enum class

Перечисления ( enum ) используются в C++-коде достаточно часто. Оно и понятно: возможность ассоциировать некое значение с понятным именем в ряде случаев довольно ценна. Однако с использованием перечислений сопряжены некоторые (хорошо известные) проблемы. Нет, они не всегда приводят к ошибкам, и если всегда быть внимательным, то и вспоминать о них, об этих проблемах, не придётся. Однако программисты не всегда внимательны, поэтому нововведение в C++11, называемое классы-перечисления ( enum class ), является действительно ценным. Предназначение класса-перечисления именно в том, чтобы избавиться от проблем классического enum . Мы будем вспоминать эти проблемы последовательно, и тут же анализировать то, что предлагает C++11. Итак:

enum error < file_not_found , cannot_open , invalid_coding >; int main()

Как видим, конкретное значение file_not_found принадлежит внешнему по отношению к enum пространству имён. Значит, если enum error определён в глобальном пространстве имён, то и его значения будут глобальны. Это не очень хорошо, хотя бы только потому, что возможен конфликт между именами в разных enum :

enum error < file_not_found , cannot_open , invalid_coding >; // Где-то в другом месте. enum error2 < file_not_found , cannot_open , invalid_coding >;

Компилятор ругнётся, и правильно сделает:

error: conflicting declaration ‘file_not_found’ error: ‘file_not_found’ has a previous declaration as ‘error file_not_found’

Класс-перечисление решает эту проблему.
enum class error < // Мы просто добавили слово class после enum, только и всего. file_not_found , cannot_open , invalid_coding >; int main()

Как видим, значение file_not_found теперь принадлежит области видимости error , и это гораздо логичнее. Понятно, что конфликта имён уже нет:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *