Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/enum"

From cppreference.com
< cpp‎ | language
m (Notes: c → tt)
(Removed the keyword revboxes.)
 
(31 intermediate revisions by 13 users not shown)
Line 9: Line 9:
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|num=1|1=
 
{{sdsc|num=1|1=
{{spar|enum-key attr}}{{mark optional}} {{spar|enum-head-name}}{{mark optional}} {{spar|enum-base}}{{mark optional}}<br>{{ttb|{}} {{spar|enumerator-list}}{{mark optional}} {{ttb|}<!---->}}
+
{{spar|enum-key}} {{spar optional|attr}} {{spar optional|enum-head-name}} {{spar optional|enum-base}}<br>{{ttb|{}} {{spar optional|enumerator-list}} {{ttb|}<!---->}}
 
}}
 
}}
 
{{sdsc|num=2|1=
 
{{sdsc|num=2|1=
{{spar|enum-key attr}}{{mark optional}} {{spar|enum-head-name}}{{mark optional}} {{spar|enum-base}}{{mark optional}}<br>{{ttb|{}} {{spar|enumerator-list}} {{ttb|, }<!---->}}
+
{{spar|enum-key}} {{spar optional|attr}} {{spar optional|enum-head-name}} {{spar optional|enum-base}}<br>{{ttb|{}} {{spar|enumerator-list}} {{ttb|, }<!---->}}
 
}}
 
}}
 
{{sdsc|num=3|notes={{mark since c++11}}|1=
 
{{sdsc|num=3|notes={{mark since c++11}}|1=
{{spar|enum-key attr}}{{mark optional}} {{spar|enum-head-name enum-base}}{{mark optional}} {{ttb|;}}
+
{{spar|enum-key}} {{spar optional|attr}} {{spar|enum-head-name}} {{spar optional|enum-base}} {{ttb|;}}
 
}}
 
}}
 
{{sdsc end}}
 
{{sdsc end}}
@1@ ''enum-specifier'', which appears in {{spar|decl-specifier-seq}} of the {{rlp|declarations|declaration}} syntax: defines the enumeration type and its enumerators.
+
@1@ {{spar|enum-specifier}}, which appears in {{spar|decl-specifier-seq}} of the {{rlp|declarations|declaration}} syntax: defines the enumeration type and its enumerators.
 
@2@ A trailing comma can follow the {{spar|enumerator-list}}.
 
@2@ A trailing comma can follow the {{spar|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.
+
@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.
  
 
{{par begin}}
 
{{par begin}}
Line 31: Line 31:
 
|since2=c++11|rev2=the name of the enumeration that's being declared, optionally preceded by a {{spar|nested-name-specifier}}: sequence of names and scope-resolution operators {{tt|::}}, ending with scope-resolution operator. It can only be omitted in unscoped non-opaque enumeration declarations.<br>
 
|since2=c++11|rev2=the name of the enumeration that's being declared, optionally preceded by a {{spar|nested-name-specifier}}: sequence of names and scope-resolution operators {{tt|::}}, ending with scope-resolution operator. It can only be omitted in unscoped non-opaque enumeration declarations.<br>
 
{{spar|nested-name-specifier}} may only appear if the enumeration name is present and this declaration is a redeclaration. For opaque enumeration declarations, {{spar|nested-name-specifier}} can only appear before the name of the enumeration in {{rlp|template specialization|explicit specialization declarations}}.<br>
 
{{spar|nested-name-specifier}} may only appear if the enumeration name is present and this declaration is a redeclaration. For opaque enumeration declarations, {{spar|nested-name-specifier}} can only appear before the name of the enumeration in {{rlp|template specialization|explicit specialization declarations}}.<br>
If {{spar|nested-name-specifier}} is present, the ''enum-specifier'' cannot refer to an enumeration  merely inherited or introduced by a {{rlp|using declaration|using-declaration}}, and the ''enum-specifier'' can only appear in a namespace enclosing the previous declaration. In such cases, {{spar|nested-name-specifier}} cannot begin with a {{rlp|decltype}} specifier.}}}}
+
If {{spar|nested-name-specifier}} is present, the ''enum-specifier'' cannot refer to an enumeration  merely inherited or introduced by a {{rlp|using declaration|{{c/core|using}} declaration}}, and the ''enum-specifier'' can only appear in a namespace enclosing the previous declaration. In such cases, {{spar|nested-name-specifier}} cannot begin with a {{rlp|decltype}} specifier.}}}}
 
{{par|{{spar|enum-base}}|{{mark since c++11}} colon ({{ttb|:}}), followed by a {{spar|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}}
 
{{par|{{spar|enum-base}}|{{mark since c++11}} colon ({{ttb|:}}), followed by a {{spar|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}}
{{par|{{spar|enumerator-list}}|comma-separated list of enumerator definitions, each of which is either simply a unique {{spar|identifier}}, which becomes the name of the enumerator, or a unique identifier with an initializer: {{spar|identifier}} {{ttb|{{=}}}} {{spar|constexpr}}. {{rev inl|since=c++17|In either case, the {{spar|identifier}} can be directly followed by an optional {{rlp|attributes|attribute specifier sequence}}.}}}}
+
{{par|{{spar|enumerator-list}}|comma-separated list of enumerator definitions, each of which is either simply a unique {{spar|identifier}}, which becomes the name of the enumerator, or a unique identifier with a constant expression: {{spar|identifier}} {{ttb|{{=}}}} {{spar|constant-expression}}. {{rev inl|since=c++17|In either case, the {{spar|identifier}} can be directly followed by an optional {{rlp|attributes|attribute specifier sequence}}.}}}}
 
{{par end}}
 
{{par end}}
  
Line 41: Line 41:
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|num=1|1=
 
{{sdsc|num=1|1=
{{ttb|enum}} {{spar|name}}{{mark optional}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} {{spar|enumerator}} {{ttb|{{=}}}} {{spar|constexpr}} {{ttb|,}} ... {{ttb|}<!---->}}  
+
{{ttb|enum}} {{spar optional|name}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} {{spar|enumerator}} {{ttb|{{=}}}} {{spar|constant-expression}} {{ttb|,}} ... {{ttb|}<!---->}}  
 
}}
 
}}
 
{{sdsc|num=2|notes={{mark since c++11}}|1=
 
{{sdsc|num=2|notes={{mark since c++11}}|1=
{{ttb|enum}} {{spar|name}}{{mark optional}} {{ttb|:}} {{spar|type}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} ... {{ttb|}<!---->}}
+
{{ttb|enum}} {{spar optional|name}} {{ttb|:}} {{spar|type}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} ... {{ttb|}<!---->}}
 
}}
 
}}
 
{{sdsc|num=3|notes={{mark since c++11}}|1=
 
{{sdsc|num=3|notes={{mark since c++11}}|1=
Line 50: Line 50:
 
}}
 
}}
 
{{sdsc end}}
 
{{sdsc end}}
@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 {{c|int}} unless the value of an enumerator cannot fit in an {{c|int}} or {{c|unsigned int}}. If the {{spar|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).
+
@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 {{c/core|int}} unless the value of an enumerator cannot fit in an {{c/core|int}} or {{c/core|unsigned int}}. If the {{spar|enumerator-list}} is empty, the underlying type is as if the enumeration had a single enumerator with value {{c|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.
 
@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.
 
@3@ Opaque enum declaration for an unscoped enumeration must specify the name and the underlying type.
Line 67: Line 67:
 
}}
 
}}
  
Each enumerator is associated with a value of the underlying type. When initializers are provided in the {{spar|enumerator-list}}, the values of enumerators are defined by those initializers. If the first enumerator does not have an initializer, the associated value is zero. For any other enumerator whose definition does not have an initializer, the associated value is the value of the previous enumerator plus one.
+
Each enumerator is associated with a value of the underlying type. When {{ttb|1==}} are provided in an {{spar|enumerator-list}}, the values of enumerators are defined by those associated {{spar|constant-expression}}s. If the first enumerator does not have {{ttb|1==}}, the associated value is zero. For any other enumerator whose definition does not have an {{ttb|1==}}, the associated value is the value of the previous enumerator plus one.
 
{{source|1=enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
 
{{source|1=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}}
 
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12}}
 
Values of unscoped enumeration type are {{rlp|implicit conversion#Integral promotion|implicitly-convertible}} to integral types. If the underlying type is not fixed, the value is convertible to the first type from the following list able to hold their entire value range: {{c|int}}, {{c|unsigned int}}, {{c|long}}, {{c|unsigned long}}, {{rev inl|since=c++11|{{c|long long}}, or {{c|unsigned long long}}, extended integer types with higher conversion rank (in rank order, signed given preference over unsigned)}}. If the underlying type is fixed, the values can be converted to their underlying type (preferred in {{rlp|overload resolution}}), which can then be {{rlp|implicit conversion#Integral promotion|promoted}}.
 
{{source|1=
 
enum color { red, yellow, green = 20, blue };
 
color col = red;
 
int n = blue; // n == 21
 
}}
 
 
Values of integer, floating-point, and enumeration types can be converted by {{rlpt|static_cast}} or {{rlp|explicit cast}}, to any enumeration type. If the underlying type is not fixed and the source value is out of range, the behavior is undefined. (The source value, as converted to the enumeration's underlying type if floating-point, is in range if it would fit in the smallest {{rlp|bit field|bit-field}} large enough to hold all enumerators of the target enumeration.) Otherwise, the result is the same as the result of {{rlp|implicit conversion}} to the underlying type.
 
 
Note that the value after such conversion may not necessarily equal any of the named enumerators defined for the enumeration.
 
{{source|1=
 
enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7
 
access_t rwe = static_cast<access_t>(7);
 
assert((rwe & read) && (rwe & write) && (rwe & exec));
 
 
access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766
 
access_t y = static_cast<access_t>(8);  // undefined behavior since CWG 1766
 
 
enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX]
 
foo x = foo(-1); // undefined behavior since CWG 1766,
 
                // even if foo's underlying type is unsigned int
 
}}
 
  
 
The {{spar|name}} of an unscoped enumeration may be omitted: such declaration only introduces the enumerators into the enclosing scope:
 
The {{spar|name}} of an unscoped enumeration may be omitted: such declaration only introduces the enumerators into the enclosing scope:
Line 134: Line 111:
 
}}
 
}}
 
}}
 
}}
 +
 +
====Enumeration name for linkage purposes====
 +
An unnamed enumeration that does not have a {{rlps|typedef#typedef name for linkage purposes}} and that has an enumerator is denoted, for {{rlp|storage duration|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===
 
===Scoped enumerations===
Line 139: Line 119:
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|num=1|1=
 
{{sdsc|num=1|1=
{{ttb|enum struct{{!}}class}} {{spar|name}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} ... {{ttb|}<!---->}}
+
{{ttb|enum struct{{!}}class}} {{spar|name}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} ... {{ttb|}<!---->}}
 
}}
 
}}
 
{{sdsc|num=2|1=
 
{{sdsc|num=2|1=
{{ttb|enum struct{{!}}class}} {{spar|name}} {{ttb|:}} {{spar|type}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constexpr}} {{ttb|,}} ... {{ttb|}<!---->}}
+
{{ttb|enum struct{{!}}class}} {{spar|name}} {{ttb|:}} {{spar|type}} {{ttb|{}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} {{spar|enumerator}} {{ttb|1==}} {{spar|constant-expression}} {{ttb|,}} ... {{ttb|}<!---->}}
 
}}
 
}}
 
{{sdsc|num=3|1=
 
{{sdsc|num=3|1=
Line 152: Line 132:
 
{{sdsc end}}
 
{{sdsc end}}
  
@1@ declares a scoped enumeration type whose underlying type is {{c|int}} (the keywords {{c|class}} and {{c|struct}} are exactly equivalent)
+
@1@ declares a scoped enumeration type whose underlying type is {{c/core|int}} (the keywords {{c/core|class}} and {{c/core|struct}} are exactly equivalent)
 
@2@ declares a scoped enumeration type whose underlying type is {{spar|type}}
 
@2@ declares a scoped enumeration type whose underlying type is {{spar|type}}
@3@ opaque enum declaration for a scoped enumeration whose underlying type is {{c|int}}
+
@3@ opaque enum declaration for a scoped enumeration whose underlying type is {{c/core|int}}
 
@4@ opaque enum declaration for a scoped enumeration whose underlying type is {{spar|type}}
 
@4@ opaque enum declaration for a scoped enumeration whose underlying type is {{spar|type}}
  
 
Each {{spar|enumerator}} becomes a named constant of the enumeration's type (that is, {{spar|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 {{rlpt|static_cast}} may be used to obtain the numeric value of the enumerator.
 
Each {{spar|enumerator}} becomes a named constant of the enumeration's type (that is, {{spar|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 {{rlpt|static_cast}} may be used to obtain the numeric value of the enumerator.
{{source|1=
+
{{example
enum class Color { red, green = 20, blue };
+
|code=
Color r = Color::blue;
+
#include <iostream>
  
switch(r)
+
int main()
 
{
 
{
     case Color::red : std::cout << "red\n";  break;
+
     enum class Color { red, green = 20, blue };
    case Color::green: std::cout << "green\n"; break;
+
     Color r = Color::blue;
     case Color::blue : std::cout << "blue\n"; break;
+
}
+
  
// int n = r; // error: no implicit conversion from scoped enum to int
+
    switch(r)
int n = static_cast<int>(r); // OK, n = 21
+
    {
 +
        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
 +
}
 
}}
 
}}
 
}}
 
}}
Line 176: Line 163:
 
{{rrev|since=c++17|
 
{{rrev|since=c++17|
 
An enumeration can be initialized from an integer without a cast, using {{rlp|list initialization}}, if all of the following are true:
 
An enumeration can be initialized from an integer without a cast, using {{rlp|list initialization}}, if all of the following are true:
* the initialization is direct-list-initialization
+
* The initialization is direct-list-initialization.
* the initializer list has only a single element
+
* The initializer list has only a single element.
* the enumeration is either scoped or unscoped with underlying type fixed
+
* The enumeration is either scoped or unscoped with underlying type fixed.
* the conversion is non-narrowing
+
* The conversion is non-narrowing.
  
 
This makes it possible to introduce new integer types (e.g. {{tt|SafeInt}}) that enjoy the same existing calling conventions as their underlying integer types, even on ABIs that penalize passing/returning structures by value.
 
This makes it possible to introduce new integer types (e.g. {{tt|SafeInt}}) that enjoy the same existing calling conventions as their underlying integer types, even on ABIs that penalize passing/returning structures by value.
Line 204: Line 191:
  
 
{{rrev|since=c++20|
 
{{rrev|since=c++20|
===Using-enum-declaration===
+
==={{c/core|using enum}} declaration===
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|notes={{mark since c++20}}|
 
{{sdsc|notes={{mark since c++20}}|
{{ttb|using enum}} {{spar|nested-name-specifier}}{{mark optional}} {{spar|name}} {{ttb|;}}
+
{{ttb|using enum}} {{spar|using-enum-declarator}} {{ttb|;}}
 
}}
 
}}
 
{{sdsc end}}
 
{{sdsc end}}
  
{{spar|nested-name-specifier}}{{mark optional}} {{spar|name}} must name a non-{{rlp|dependent name#Dependent types|dependent}} enumeration type. The enumeration declarations are found by ordinary {{rlp|qualified lookup|qualified}} or {{rlp|unqualified lookup|unqualified}} lookup, depending on whether {{spar|nested-name-specifier}} is present.
+
{{par begin}}
 +
{{par|{{spar|declarator}}|a (possibly qualified) {{rlp|identifiers|identifier}} or {{rlp|templates#Template identifiers|simple template identifiers}}}}
 +
{{par end}}
 +
 
 +
 
 +
{{spar|declarator}} must name a non-{{rlp|dependent name#Dependent types|dependent}} enumeration type. The enumeration declarations are found by type-only ordinary {{rlp|qualified lookup|qualified}} or {{rlp|unqualified lookup|unqualified}} lookup, depending on whether {{spar|declarator}} is qualified.
 +
 
 +
{{source|1=
 +
enum E { x };
 +
 
 +
void f()
 +
{
 +
    int E;
 +
    using enum E; // OK
 +
}
 +
 
 +
using F = E;
 +
using enum F; // OK
 +
 
 +
template<class T>
 +
using EE = T;
 +
 
 +
void g()
 +
{
 +
    using enum EE<E>; // OK
 +
}
 +
}}
  
A using-enum-declaration introduces the enumerator names of the named enumeration as if by a {{rlp|using declaration|using-declaration}} for each enumerator. When in class scope, a using-enum-declaration adds the enumerators of the named enumeration as members to the scope, making them accessible for member lookup.
+
A {{c/core|using enum}} declaration introduces the enumerator names of the named enumeration as if by a {{rlp|using declaration|{{c/core|using}} declaration}} for each enumerator. When in class scope, a {{c/core|using enum}} declaration adds the enumerators of the named enumeration as members to the scope, making them accessible for member lookup.
  
 
{{source|1=
 
{{source|1=
Line 231: Line 244:
 
}}
 
}}
  
Two using-enum-declarations that introduce two enumerators of the same name conflict.
+
Two {{c/core|using enum}} declarations that introduce two enumerators of the same name conflict.
 
{{source|1=
 
{{source|1=
 
enum class fruit { orange, apple };
 
enum class fruit { orange, apple };
Line 245: Line 258:
  
 
===Notes===
 
===Notes===
 +
Values of unscoped enumeration type can be {{rlp|implicit conversion#Promotion from enumeration types|promoted}} or {{rlp|implicit conversion#Integral conversions|converted}} to integral types:
 +
{{source|1=
 +
enum color { red, yellow, green = 20, blue };
 +
color col = red;
 +
int n = blue; // n == 21
 +
}}
 +
 +
Values of integer, floating-point, and enumeration types can be converted to any enumeration type by using {{rlpt|static_cast}}. Note that the value after such conversion may not necessarily equal any of the named enumerators defined for the enumeration:
 +
{{source|1=
 +
enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7
 +
access_t rwe = static_cast<access_t>(7);
 +
assert((rwe & read) && (rwe & write) && (rwe & exec));
 +
 +
access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766
 +
access_t y = static_cast<access_t>(8);  // undefined behavior since CWG 1766
 +
 +
enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX]
 +
foo x = foo(-1); // undefined behavior since CWG 1766,
 +
                // even if foo's underlying type is unsigned int
 +
}}
 +
 
{{ftm begin|core=1|std=1|comment=1}}
 
{{ftm begin|core=1|std=1|comment=1}}
 
{{ftm|std=C++17|value=201411L|__cpp_enumerator_attributes|{{rlp|attributes|Attributes}} for enumerators}}
 
{{ftm|std=C++17|value=201411L|__cpp_enumerator_attributes|{{rlp|attributes|Attributes}} for enumerators}}
 
{{ftm|std=C++20|value=201907L|__cpp_using_enum|[[#Using-enum-declaration|{{tt|using enum}}]]}}
 
{{ftm|std=C++20|value=201907L|__cpp_using_enum|[[#Using-enum-declaration|{{tt|using enum}}]]}}
 
{{ftm end}}
 
{{ftm end}}
 +
 +
===Keywords===
 +
{{ltt|cpp/keyword/enum}},
 +
{{ltt|cpp/keyword/struct}},
 +
{{ltt|cpp/keyword/class}},
 +
{{ltt|cpp/keyword/using}}
  
 
===Example===
 
===Example===
Line 375: Line 415:
 
{{dr list item|wg=cwg|dr=2530|std=C++98|before=an enumerator list could contain multiple<br>enumerators with the same identifier|after=prohibited}}
 
{{dr list item|wg=cwg|dr=2530|std=C++98|before=an enumerator list could contain multiple<br>enumerators with the same identifier|after=prohibited}}
 
{{dr list item|wg=cwg|dr=2590|std=C++98|before=the size, value representation and alignment requirements<br>of an enumeration did not depend on its underlying type|after=all of them are identical to<br>those of the underlying type}}
 
{{dr list item|wg=cwg|dr=2590|std=C++98|before=the size, value representation and alignment requirements<br>of an enumeration did not depend on its underlying type|after=all of them are identical to<br>those of the underlying type}}
{{dr list item|wg=cwg|dr=2621|std=C++20|before=it was unclear how the enumeration names<br>are found in using-enum-declarations|after=they are found using<br>ordinary name lookup}}
+
{{dr list item|wg=cwg|dr=2621|std=C++20|before=the enumeration name lookup used in<br>{{c/core|using enum}} declarations was unclear|after=made clear}}
 +
{{dr list item|wg=cwg|dr=2877|std=C++20|before=the enumeration name lookup used in<br>{{c/core|using enum}} declarations was not type-only|after=made type-only}}
 
{{dr list end}}
 
{{dr list end}}
 +
 +
===References===
 +
{{ref std c++23}}
 +
{{ref std|section=9.7.1|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++20}}
 +
{{ref std|section=9.7.1|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++17}}
 +
{{ref std|section=10.2|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++14}}
 +
{{ref std|section=7.2|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++11}}
 +
{{ref std|section=7.2|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++03}}
 +
{{ref std|section=7.2|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
 +
{{ref std c++98}}
 +
{{ref std|section=7.2|title=Enumeration declarations|id=dcl.enum}}
 +
{{ref std end}}
  
 
===See also===
 
===See also===

Latest revision as of 17:12, 14 August 2024

 
 
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
Fundamental types
Enumeration types
Function 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).

Contents

[edit] 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)

[edit] 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.

[edit] 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};        // OK as of C++17 (direct-list-initialization)
byte c = {42};     // error
byte d = byte{42}; // OK as of C++17; same value as b
byte e{-1};        // error
 
struct A { byte b; };
A a1 = {{42}};     // error (copy-list-initialization of a constructor parameter)
A a2 = {byte{42}}; // OK as of C++17
 
void f(byte);
f({42}); // error (copy-list-initialization of a function parameter)
 
enum class Handle : std::uint32_t { Invalid = 0 };
Handle h{42}; // OK as of C++17
(since C++17)


using enum declaration

using enum using-enum-declarator ; (since C++20)
declarator - a (possibly qualified) identifier or simple template identifiers


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.

enum E { x };
 
void f()
{
    int E;
    using enum E; // OK
}
 
using F = E;
using enum F; // OK
 
template<class T>
using EE = T;
 
void g()
{
    using enum EE<E>; // OK
}

A using enum declaration introduces the enumerator names of the named enumeration as if by a using declaration for each enumerator. When in class scope, a using enum declaration adds the enumerators of the named enumeration as members to the scope, making them accessible for member lookup.

enum class fruit { orange, apple };
 
struct S
{
    using enum fruit; // OK: introduces orange and apple into S
};
 
void f()
{
    S s;
    s.orange;  // OK: names fruit::orange
    S::orange; // OK: names fruit::orange
}

Two using enum declarations that introduce two enumerators of the same name conflict.

enum class fruit { orange, apple };
enum class color { red, orange };
 
void f()
{
    using enum fruit;    // OK
    // using enum color; // error: color::orange and fruit::orange conflict
}
(since C++20)

[edit] Notes

Values of unscoped enumeration type can be promoted or converted to integral types:

enum color { red, yellow, green = 20, blue };
color col = red;
int n = blue; // n == 21

Values 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:

enum access_t { read = 1, write = 2, exec = 4 }; // enumerators: 1, 2, 4 range: 0..7
access_t rwe = static_cast<access_t>(7);
assert((rwe & read) && (rwe & write) && (rwe & exec));
 
access_t x = static_cast<access_t>(8.0); // undefined behavior since CWG 1766
access_t y = static_cast<access_t>(8);   // undefined behavior since CWG 1766
 
enum foo { a = 0, b = UINT_MAX }; // range: [0, UINT_MAX]
foo x = foo(-1); // undefined behavior since CWG 1766,
                 // even if foo's underlying type is unsigned int
Feature-test macro Value Std Feature
__cpp_enumerator_attributes 201411L (C++17) Attributes for enumerators
__cpp_using_enum 201907L (C++20) using enum

[edit] Keywords

enum, struct, class, using

[edit] Example

#include <cstdint>
#include <iostream>
 
// enum that takes 16 bits
enum smallenum: std::int16_t
{
    a,
    b,
    c
};
 
// color may be red (value 0), yellow (value 1), green (value 20), or blue (value 21)
enum color
{
    red,
    yellow,
    green = 20,
    blue
};
 
// altitude may be altitude::high or altitude::low
enum class altitude: 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 3
enum
{
    d,
    e,
    f = e + 2
};
 
// enumeration types (both scoped and unscoped) can have overloaded operators
std::ostream& operator<<(std::ostream& os, color c)
{
    switch(c)
    {
        case red   : os << "red";    break;
        case yellow: os << "yellow"; break;
        case green : os << "green";  break;
        case blue  : os << "blue";   break;
        default    : os.setstate(std::ios_base::failbit);
    }
    return os;
}
 
std::ostream& operator<<(std::ostream& os, altitude al)
{
    return os << static_cast<char>(al);
}
 
// The scoped enum (C++11) can be partially emulated in earlier C++ revisions:
 
enum struct E11 { x, y }; // since C++11
 
struct E98 { enum { x, y }; }; // OK in pre-C++11
 
namespace N98 { enum { x, y }; } // OK in pre-C++11
 
struct S98 { static const int x = 0, y = 1; }; // OK in pre-C++11
 
void emu()
{
    std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4
}
 
namespace cxx20
{
    enum class long_long_long_name { x, y };
 
    void using_enum_demo()
    {
        std::cout << "C++20 `using enum`: __cpp_using_enum == ";
        switch (auto rnd = []{return long_long_long_name::x;}; rnd())
        {
#if defined(__cpp_using_enum)
            using enum long_long_long_name;
            case x: std::cout << __cpp_using_enum << "; x\n"; break;
            case y: std::cout << __cpp_using_enum << "; y\n"; break;
#else
            case long_long_long_name::x: std::cout << "?; x\n"; break;
            case long_long_long_name::y: std::cout << "?; y\n"; break;
#endif
        }
    }
}
 
int main()
{
    color col = red;
    altitude a;
    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

[edit] 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-base
only 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
using enum declarations was unclear
made clear
CWG 2877 C++20 the enumeration name lookup used in
using enum declarations was not type-only
made type-only

[edit] 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]

[edit] See also

(C++11)
checks if a type is an enumeration type
(class template) [edit]
checks if a type is a scoped enumeration type
(class template) [edit]
obtains the underlying integer type for a given enumeration type
(class template) [edit]
converts an enumeration to its underlying type
(function template) [edit]
C documentation for Enumerations