Enumeration declaration
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) | |||||||
| enum-key | - |
| ||||
| attr | - | (since C++11) optional sequence of any number of attributes | ||||
| enum-head-name | - |
| ||||
| 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) | |||||||
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).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
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
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
int4) 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 Run this code #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:
This makes it possible to introduce new integer types (e.g. enum byte : unsigned char {}; // byte is a new integer type; see also std::byte (C++17)
byte b{42}; |