Enumeration declaration - cppreference.com

An enumeration is a distinct type whose value is restricted to a range of values (see below for details), which may include several explicitly named constants ("enumerators").

The values of the constants are values of an integral type known as the underlying type of the enumeration. An enumeration has the same

size

,

value representation

, and

alignment requirements

as its underlying type. Furthermore, each value of an enumeration has the same representation as the corresponding value of the underlying type.

An enumeration is (re)declared using the following syntax:

enum-keyattr(optional)enum-head-name(optional)enum-base(optional)
{enumerator-list(optional)}(1) enum-keyattr(optional)enum-head-name(optional)enum-base(optional)
{enumerator-list, }(2) enum-keyattr(optional)enum-head-nameenum-base(optional);(3) (since C++11)1)enum-specifier, which appears in decl-specifier-seq of the

declaration

syntax: defines the enumeration type and its enumerators.

2) A trailing comma can follow the enumerator-list.

3) Opaque enum declaration: defines the enumeration type but not its enumerators: after this declaration, the type is a complete type and its size is known.

enum-key- enum

(until C++11)one of enum, enum class, or enum struct

(since C++11)attr- (since C++11) optional sequence of any number of

attributes

enum-head-name- the name of the enumeration that's being declared, it can be omitted.

(until C++11)the name of the enumeration that's being declared, optionally preceded by a nested-name-specifier: sequence of names and scope-resolution operators ::, ending with scope-resolution operator. It can only be omitted in unscoped non-opaque enumeration declarations.
nested-name-specifier may only appear if the enumeration name is present and this declaration is a redeclaration. For opaque enumeration declarations, nested-name-specifier can only appear before the name of the enumeration in

explicit specialization declarations

.
If nested-name-specifier is present, the enum-specifier cannot refer to an enumeration merely inherited or introduced by a

using declaration

, and the enum-specifier can only appear in a namespace enclosing the previous declaration. In such cases, nested-name-specifier cannot begin with a

decltype

specifier.

(since C++11)enum-base- (since C++11) colon (:), followed by a type-specifier-seq that names an integral type (if it is cv-qualified, qualifications are ignored) that will serve as the fixed underlying type for this enumeration type enumerator-list- comma-separated list of enumerator definitions, each of which is either simply a unique identifier, which becomes the name of the enumerator, or a unique identifier with a constant expression: identifier=constant-expression. In either case, the identifier can be directly followed by an optional

attribute specifier sequence

.(since C++17)There are two distinct kinds of enumerations: unscoped enumeration (declared with the enum-keyenum) and scoped enumeration (declared with the enum-keyenum class or enum struct).

Unscoped enumerations

enumname(optional){enumerator=constant-expression,enumerator=constant-expression, ... }(1) enumname(optional):type{enumerator=constant-expression,enumerator=constant-expression, ... }(2) (since C++11)enumname:type;(3) (since C++11)1) Declares an unscoped enumeration type whose underlying type is not fixed (in this case, the underlying type is an implementation-defined integral type that can represent all enumerator values; this type is not larger than int unless the value of an enumerator cannot fit in an int or unsignedint. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. If no integral type can represent all the enumerator values, the enumeration is ill-formed).

2) Declares an unscoped enumeration type whose underlying type is fixed.

3) Opaque enum declaration for an unscoped enumeration must specify the name and the underlying type.

Each enumerator becomes a named constant of the enumeration's type (that is, name), visible in the enclosing scope, and can be used whenever constants are required.

enumColor{red,green,blue};Colorr=red;switch(r){casered:std::cout<<"red\n";break;casegreen:std::cout<<"green\n";break;caseblue:std::cout<<"blue\n";break;}Each enumerator is associated with a value of the underlying type. When = are provided in an enumerator-list, the values of enumerators are defined by those associated constant-expressions. If the first enumerator does not have =, the associated value is zero. For any other enumerator whose definition does not have an =, the associated value is the value of the previous enumerator plus one.

enumFoo{a,b,c=10,d,e=1,f,g=f+c};//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12The name of an unscoped enumeration may be omitted: such declaration only introduces the enumerators into the enclosing scope:

enum{a,b,c=0,d=a+2};// defines a = 0, b = 1, c = 0, d = 2When an unscoped enumeration is a class member, its enumerators may be accessed using class member access operators . and ->:

structX{enumdirection{left='l',right='r'};};Xx;X*p=&x;inta=X::direction::left;// allowed only in C++11 and laterintb=X::left;intc=x.left;intd=p->left;In the

declaration specifiers

of a

member declaration

, the sequence

enumenum-head-name:is always parsed as a part of enumeration declaration:

structS{enumE1:int{};enumE1:int{};// error: redeclaration of enumeration,// NOT parsed as a zero-length bit-field of type enum E1};enumE2{e1};voidf(){false?newenumE2:int();// OK: 'int' is NOT parsed as the underlying type}(since C++11)Enumeration name for linkage purposes

An unnamed enumeration that does not have a

typedef name for linkage purposes

and that has an enumerator is denoted, for

linkage purposes

, by its underlying type and its first enumerator; such an enumeration is said to have an enumerator as a name for linkage purposes.

Scoped enumerations

enum struct|classname{enumerator=constant-expression,enumerator=constant-expression, ... }(1) enum struct|classname:type{enumerator=constant-expression,enumerator=constant-expression, ... }(2) enum struct|classname;(3) enum struct|classname:type;(4) 1) declares a scoped enumeration type whose underlying type is int (the keywords class and struct are exactly equivalent)

2) declares a scoped enumeration type whose underlying type is type

3) opaque enum declaration for a scoped enumeration whose underlying type is int

4) opaque enum declaration for a scoped enumeration whose underlying type is type

Each enumerator becomes a named constant of the enumeration's type (that is, name), which is contained within the scope of the enumeration, and can be accessed using scope resolution operator. There are no implicit conversions from the values of a scoped enumerator to integral types, although

static_cast

may be used to obtain the numeric value of the enumerator.

Run this code

#include<iostream>intmain(){enumclassColor{red,green=20,blue};Colorr=Color::blue;switch(r){caseColor::red:std::cout<<"red\n";break;caseColor::green:std::cout<<"green\n";break;caseColor::blue:std::cout<<"blue\n";break;}// int n = r; // error: no implicit conversion from scoped enum to intintn=static_cast<int>(r);// OK, n = 21std::cout<<n<<'\n';// prints 21}(since C++11)An enumeration can be initialized from an integer without a cast, using

list initialization

, if all of the following are true:

The initialization is direct-list-initialization.

The initializer list has only a single element.

The enumeration is either scoped or unscoped with underlying type fixed.

The conversion is non-narrowing.

This makes it possible to introduce new integer types (e.g. SafeInt) that enjoy the same existing calling conventions as their underlying integer types, even on ABIs that penalize passing/returning structures by value.

enumbyte:unsignedchar{};// byte is a new integer type; see also std::byte (C++17)byteb{42};// OK as of C++17 (direct-list-initialization)bytec={42};// errorbyted=byte{42};// OK as of C++17; same value as bbytee{-1};// errorstructA{byteb;};Aa1={{42}};// error (copy-list-initialization of a constructor parameter)Aa2={byte{42}};// OK as of C++17voidf(byte);f({42});// error (copy-list-initialization of a function parameter)enumclassHandle:std::uint32_t{Invalid=0};Handleh{42};// OK as of C++17(since C++17)using enum declaration

using enumusing-enum-declarator;(since C++20)
declarator must name a non-

dependent

enumeration type. The enumeration declarations are found by type-only ordinary

qualified

or

unqualified

lookup, depending on whether declarator is qualified.

enumE{x};voidf(){intE;usingenumE;// OK}usingF=E;usingenumF;// OKtemplate<classT>usingEE=T;voidg(){usingenumEE<E>;// OK}A usingenum declaration introduces the enumerator names of the named enumeration as if by a

using declaration

for each enumerator. When in class scope, a usingenum declaration adds the enumerators of the named enumeration as members to the scope, making them accessible for member lookup.

enumclassfruit{orange,apple};structS{usingenumfruit;// OK: introduces orange and apple into S};voidf(){Ss;s.orange;// OK: names fruit::orangeS::orange;// OK: names fruit::orange}Two usingenum declarations that introduce two enumerators of the same name conflict.

enumclassfruit{orange,apple};enumclasscolor{red,orange};voidf(){usingenumfruit;// OK// using enum color; // error: color::orange and fruit::orange conflict}(since C++20)Notes

Values of unscoped enumeration type can be

promoted

or

converted

to integral types:

enumcolor{red,yellow,green=20,blue};colorcol=red;intn=blue;// n == 21Values of integer, floating-point, and enumeration types can be converted to any enumeration type by using

static_cast

. Note that the value after such conversion may not necessarily equal any of the named enumerators defined for the enumeration:

enumaccess_t{read=1,write=2,exec=4};// enumerators: 1, 2, 4 range: 0..7access_trwe=static_cast<access_t>(7);assert((rwe&read)&&(rwe&write)&&(rwe&exec));access_tx=static_cast<access_t>(8.0);// undefined behavior since CWG 1766access_ty=static_cast<access_t>(8);// undefined behavior since CWG 1766enumfoo{a=0,b=UINT_MAX};// range: [0, UINT_MAX]foox=foo(-1);// undefined behavior since CWG 1766,// even if foo's underlying type is unsigned intFeature-test macro ValueStdFeature

__cpp_enumerator_attributes

201411L

(C++17)

Attributes

for enumerators

__cpp_using_enum

201907L

(C++20)

using enum

Keywords

enum

,

struct

,

class

,

using

Example

Run this code

#include<cstdint>#include<iostream>// enum that takes 16 bitsenumsmallenum:std::int16_t{a,b,c};// color may be red (value 0), yellow (value 1), green (value 20), or blue (value 21)enumcolor{red,yellow,green=20,blue};// altitude may be altitude::high or altitude::lowenumclassaltitude:char{high='h',low='l',// trailing comma only allowed after CWG 518};// the constant d is 0, the constant e is 1, the constant f is 3enum{d,e,f=e+2};// enumeration types (both scoped and unscoped) can have overloaded operatorsstd::ostream&operator<<(std::ostream&os,colorc){switch(c){casered:os<<"red";break;caseyellow:os<<"yellow";break;casegreen:os<<"green";break;caseblue:os<<"blue";break;default:os.setstate(std::ios_base::failbit);}returnos;}std::ostream&operator<<(std::ostream&os,altitudeal){returnos<<static_cast<char>(al);}// The scoped enum (C++11) can be partially emulated in earlier C++ revisions:enumstructE11{x,y};// since C++11structE98{enum{x,y};};// OK in pre-C++11namespaceN98{enum{x,y};}// OK in pre-C++11structS98{staticconstintx=0,y=1;};// OK in pre-C++11voidemu(){std::cout<<(static_cast<int>(E11::y)+E98::y+N98::y+S98::y)<<'\n';// 4}namespacecxx20{enumclasslong_long_long_name{x,y};voidusing_enum_demo(){std::cout<<"C++20 `using enum`: __cpp_using_enum == ";switch(autornd=[]{returnlong_long_long_name::x;};rnd()){#if defined(__cpp_using_enum)usingenumlong_long_long_name;casex:std::cout<<__cpp_using_enum<<"; x\n";break;casey:std::cout<<__cpp_using_enum<<"; y\n";break;#elsecaselong_long_long_name::x:std::cout<<"?; x\n";break;caselong_long_long_name::y:std::cout<<"?; y\n";break;#endif}}}intmain(){colorcol=red;altitudea;a=altitude::low;std::cout<<"col = "<<col<<'\n'<<"a = "<<a<<'\n'<<"f = "<<f<<'\n';cxx20::using_enum_demo();}Possible output:

col = red a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior

CWG 377

C++98 the behavior was unspecified when no integral
type can represent all the enumerator values the enumeration is ill-
formed in this case

CWG 518

C++98 a trailing comma was not allowed after the enumerator list allowed

CWG 1514

C++11 a redefinition of enumeration with fixed underlying type
could be parsed as a bit-field in a class member declaration always parsed as a redefinition

CWG 1638

C++11 grammar of opaque enumeration declaration
prohibited use for template specializations nested-name-specifier
permitted

CWG 1766

C++98 casting an out-of-range value to an enumeration
without fixed underlying type had an unspecified result the behavior is undefined

CWG 1966

C++11 the resolution of

CWG issue 1514

made the :
of a conditional expression part of enum-baseonly apply the resolution to
member declaration specifiers

CWG 2156

C++11 enum definitions could define
enumeration types by using-declarations prohibited

CWG 2157

C++11 the resolution of

CWG issue 1966

did
not cover qualified enumeration names covered

CWG 2530

C++98 an enumerator list could contain multiple
enumerators with the same identifier prohibited

CWG 2590

C++98 the size, value representation and alignment requirements
of an enumeration did not depend on its underlying type all of them are identical to
those of the underlying type

CWG 2621

C++20 the enumeration name lookup used in
usingenum declarations was unclear made clear

CWG 2877

C++20 the enumeration name lookup used in
usingenum declarations was not type-only made type-only References

C++23 standard (ISO/IEC 14882:2024):

9.7.1 Enumeration declarations [dcl.enum]

C++20 standard (ISO/IEC 14882:2020):

9.7.1 Enumeration declarations [dcl.enum]

C++17 standard (ISO/IEC 14882:2017):

10.2 Enumeration declarations [dcl.enum]

C++14 standard (ISO/IEC 14882:2014):

7.2 Enumeration declarations [dcl.enum]

C++11 standard (ISO/IEC 14882:2011):

7.2 Enumeration declarations [dcl.enum]

C++03 standard (ISO/IEC 14882:2003):

7.2 Enumeration declarations [dcl.enum]

C++98 standard (ISO/IEC 14882:1998):

7.2 Enumeration declarations [dcl.enum]

See also

is_enum

(C++11)

checks if a type is an enumeration type
(class template)

[edit]

is_scoped_enum

(C++23)

checks if a type is a scoped enumeration type
(class template)

[edit]

is_enum_type

(C++26)

checks if reflected type is an enumeration type
(function)

[edit]

is_scoped_enum_type

(C++26)

checks if reflection represents a scoped enumeration type
(function)

[edit]

is_enumerator

(C++26)

checks if reflection represents an

enumerator

(function)

[edit]

underlying_type

(C++11)

obtains the underlying integer type for a given enumeration type
(class template)

[edit]

to_underlying

(C++23)

converts an enumeration to its underlying type
(function template)

[edit]

C documentation

for Enumerations