Namespaces
Variants

Enumeration declaration

From cppreference.com
 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

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-key attr (optional) enum-head-name (optional) enum-base (optional)
{ enumerator-list (optional) }
(1)
enum-key attr (optional) enum-head-name (optional) enum-base (optional)
{ enumerator-list , }
(2)
enum-key attr (optional) enum-head-name enum-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-key enum) and scoped enumeration (declared with the enum-key enum class or enum struct).

Unscoped enumerations

enum name (optional) { enumerator = constant-expression , enumerator = constant-expression , ... } (1)
enum name (optional) : type { enumerator = constant-expression , enumerator = constant-expression , ... } (2) (since C++11)
enum name : 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 unsigned int. 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.

enum Color { red, green, blue };
Color r = red;

switch(r)
{
    case red  : std::cout << "red\n";   break;
    case green: std::cout << "green\n"; break;
    case blue : 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.

enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12

The 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 = 2

When an unscoped enumeration is a class member, its enumerators may be accessed using class member access operators . and ->:

struct X
{
    enum direction { left = 'l', right = 'r' };
};
X x;
X* p = &x;

int a = X::direction::left; // allowed only in C++11 and later
int b = X::left;
int c = x.left;
int d = p->left;

In the declaration specifiers of a member declaration, the sequence

enum enum-head-name :

is always parsed as a part of enumeration declaration:

struct S
{
    enum E1 : int {};
    enum E1 : int {}; // error: redeclaration of enumeration,
                      // NOT parsed as a zero-length bit-field of type enum E1
};

enum E2 { e1 };

void f()
{
    false ? new enum E2 : 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|class name { enumerator = constant-expression , enumerator = constant-expression , ... } (1)
enum struct|class name : type { enumerator = constant-expression , enumerator = constant-expression , ... } (2)
enum struct|class name ; (3)
enum struct|class name : 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.

#include <iostream>

int main()
{
    enum class Color { red, green = 20, blue };
    Color r = Color::blue;

    switch(r)
    {
        case Color::red  : std::cout << "red\n";   break;
        case Color::green: std::cout << "green\n"; break;
        case Color::blue : std::cout << "blue\n";  break;
    }

    // int n = r; // error: no implicit conversion from scoped enum to int
    int n = static_cast<int>(r); // OK, n = 21
    std::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.

enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17)
byte b{42};