Enumeration declaration
Una enumeración es un tipo distinto cuyo valor está restringido a un rango de valores (ver más abajo para detalles), que puede incluir varias constantes explícitamente nombradas (" enumeradores ").
Los valores de las constantes son valores de un tipo integral conocido como el tipo subyacente de la enumeración. Una enumeración tiene el mismo tamaño , representación de valor , y requisitos de alineación que su tipo subyacente. Además, cada valor de una enumeración tiene la misma representación que el valor correspondiente del tipo subyacente.
Una enumeración se (re)declara utilizando la siguiente sintaxis:
enum-key
attr
(opcional)
enum-head-name
(opcional)
enum-base
(opcional)
{
enumerator-list
(opcional)
}
|
(1) | ||||||||
enum-key
attr
(opcional)
enum-head-name
(opcional)
enum-base
(opcional)
{
enumerator-list
, }
|
(2) | ||||||||
enum-key
attr
(opcional)
enum-head-name
enum-base
(opcional)
;
|
(3) | (desde C++11) | |||||||
| enum-key | - |
|
||||
| attr | - | (desde C++11) secuencia opcional de cualquier número de atributos | ||||
| enum-head-name | - |
|
||||
| enum-base | - |
(desde C++11)
dos puntos (
:
), seguido de un
secuencia-especificadora-de-tipo
que nombra un tipo integral (si está calificado cv, las calificaciones se ignoran) que servirá como el tipo subyacente fijo para este tipo de enumeración
|
||||
| enumerator-list | - |
lista separada por comas de definiciones de enumeradores, cada una de las cuales es simplemente un
identificador
único, que se convierte en el nombre del enumerador, o un identificador único con una expresión constante:
identificador
=
expresión-constante
.
En cualquier caso, el
identificador
puede ser seguido directamente por una
secuencia especificadora de atributos
opcional.
(desde C++17)
|
Existen dos tipos distintos de enumeraciones:
enumeración no acotada
(declarada con la
enum-key
enum
) y
enumeración acotada
(declarada con la
enum-key
enum class
o
enum struct
).
Contenidos |
Enumeraciones no delimitadas
enum
nombre
(opcional)
{
enumerador
=
expresión-constante
,
enumerador
=
expresión-constante
,
...
}
|
(1) | ||||||||
enum
nombre
(opcional)
:
tipo
{
enumerador
=
expresión-constante
,
enumerador
=
expresión-constante
,
...
}
|
(2) | (desde C++11) | |||||||
enum
nombre
:
tipo
;
|
(3) | (desde C++11) | |||||||
Cada enumerador se convierte en una constante con nombre del tipo de la enumeración (es decir, name ), visible en el ámbito circundante, y puede utilizarse siempre que se requieran constantes.
Cada enumerador está asociado con un valor del tipo subyacente. Cuando se
proporcionan
=
en una
enumerator-list
, los valores de los enumeradores se definen por esas
constant-expression
s asociadas. Si el primer enumerador no tiene
=
, el valor asociado es cero. Para cualquier otro enumerador cuya definición no tenga un
=
, el valor asociado es el valor del enumerador anterior más uno.
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
El nombre de una enumeración no delimitada puede omitirse: dicha declaración solo introduce los enumeradores en el ámbito circundante:
enum { a, b, c = 0, d = a + 2 }; // define a = 0, b = 1, c = 0, d = 2
Cuando una enumeración no delimitada es un miembro de clase, sus enumeradores pueden ser accedidos usando los operadores de acceso a miembros de clase
.
y
->
:
struct X { enum direction { left = 'l', right = 'r' }; }; X x; X* p = &x; int a = X::direction::left; // permitido solo en C++11 y posteriores int b = X::left; int c = x.left; int d = p->left;
|
En los especificadores de declaración de una declaración de miembro , la secuencia
siempre se analiza como parte de la declaración de enumeración: struct S { enum E1 : int {}; enum E1 : int {}; // error: redeclaración de enumeración, // NO se analiza como un campo de bits de longitud cero de tipo enum E1 }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int' NO se analiza como el tipo subyacente } |
(desde C++11) |
Nombre de enumeración para fines de vinculación
Una enumeración sin nombre que no tiene un typedef name for linkage purposes y que tiene un enumerador se denota, para linkage purposes , por su tipo subyacente y su primer enumerador; se dice que dicha enumeración tiene un enumerador como name for linkage purposes .
Enumeraciones con ámbito
1)
declara un tipo de enumeración con ámbito cuyo tipo subyacente es
int
(las palabras clave
class
y
struct
son exactamente equivalentes)
2)
declara un tipo de enumeración con ámbito cuyo tipo subyacente es
tipo
3)
declaración de enumeración opaca para una enumeración con ámbito cuyo tipo subyacente es
int
4)
declaración de enumeración opaca para una enumeración con ámbito cuyo tipo subyacente es
tipo
Cada
enumerador
se convierte en una constante con nombre del tipo de la enumeración (es decir,
nombre
), que está contenido dentro del ámbito de la enumeración y puede accederse usando el operador de resolución de ámbito. No hay conversiones implícitas de los valores de un enumerador con ámbito a tipos enteros, aunque se puede usar
Ejecutar este código
#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 hay conversión implícita de enum con ámbito a int int n = static_cast<int>(r); // OK, n = 21 std::cout << n << '\n'; // imprime 21 } |
(desde C++11) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Una enumeración puede ser inicializada desde un entero sin conversión explícita, usando inicialización de lista , si se cumplen todas las siguientes condiciones:
Esto hace posible introducir nuevos tipos enteros (por ejemplo
enum byte : unsigned char {}; // byte es un nuevo tipo entero; ver también std::byte (C++17) byte b{42}; // OK desde C++17 (inicialización de lista directa) byte c = {42}; // error byte d = byte{42}; // OK desde C++17; mismo valor que b byte e{-1}; // error struct A { byte b; }; A a1 = {{42}}; // error (inicialización de lista por copia de un parámetro del constructor) A a2 = {byte{42}}; // OK desde C++17 void f(byte); f({42}); // error (inicialización de lista por copia de un parámetro de función) enum class Handle : std::uint32_t { Invalid = 0 }; Handle h{42}; // OK desde C++17 |
(desde C++17) |
using enum declaración
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 } Una declaración using enum introduce los nombres de los enumeradores de la enumeración nombrada como si fuera mediante una using declaración para cada enumerador. Cuando está en el ámbito de clase, una declaración using enum añade los enumeradores de la enumeración nombrada como miembros al ámbito, haciéndolos accesibles para la búsqueda de miembros. enum class fruit { orange, apple }; struct S { using enum fruit; // OK: introduce orange y apple en S }; void f() { S s; s.orange; // OK: nombra fruit::orange S::orange; // OK: nombra fruit::orange } Dos declaraciones using enum que introducen dos enumeradores con el mismo nombre entran en conflicto. enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // error: color::orange y fruit::orange entran en conflicto } |
(desde C++20) | ||||||||||||||||||||||||||
Notas
Los valores de tipo enumeración no delimitada pueden ser promocionados o convertidos a tipos integrales:
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
Los valores de tipos enteros, de punto flotante y de enumeración pueden convertirse a cualquier tipo de enumeración utilizando
static_cast
. Nótese que el valor después de dicha conversión puede no necesariamente igualar ninguno de los enumeradores definidos para la enumeración:
enum access_t { read = 1, write = 2, exec = 4 }; // enumeradores: 1, 2, 4 rango: 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); // comportamiento indefinido desde CWG 1766 access_t y = static_cast<access_t>(8); // comportamiento indefinido desde CWG 1766 enum foo { a = 0, b = UINT_MAX }; // rango: [0, UINT_MAX] foo x = foo(-1); // comportamiento indefinido desde CWG 1766, // incluso si el tipo subyacente de foo es unsigned int
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_enumerator_attributes
|
201411L
|
(C++17) | Atributos para enumeradores |
__cpp_using_enum
|
201907L
|
(C++20) |
using enum
|
Palabras clave
Ejemplo
#include <cstdint> #include <iostream> // enumeración que ocupa 16 bits enum smallenum: std::int16_t { a, b, c }; // color puede ser red (valor 0), yellow (valor 1), green (valor 20), o blue (valor 21) enum color { red, yellow, green = 20, blue }; // altitude puede ser altitude::high o altitude::low enum class altitude: char { high = 'h', low = 'l', // coma final solo permitida después de CWG 518 }; // la constante d es 0, la constante e es 1, la constante f es 3 enum { d, e, f = e + 2 }; // los tipos de enumeración (tanto con ámbito como sin ámbito) pueden tener operadores sobrecargados 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); } // La enumeración con ámbito (C++11) puede ser emulada parcialmente en revisiones anteriores de C++: enum struct E11 { x, y }; // desde C++11 struct E98 { enum { x, y }; }; // OK en pre-C++11 namespace N98 { enum { x, y }; } // OK en pre-C++11 struct S98 { static const int x = 0, y = 1; }; // OK en 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(); }
Salida posible:
col = rojo a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 377 | C++98 |
el comportamiento no estaba especificado cuando ningún tipo
entero puede representar todos los valores del enumerador |
la enumeración está mal
formada en este caso |
| CWG 518 | C++98 | no se permitía una coma final después de la lista de enumeradores | permitido |
| CWG 1514 | C++11 |
una redefinición de enumeración con tipo subyacente fijo
podía analizarse como un campo de bits en una declaración de miembro de clase |
siempre se analiza como una redefinición |
| CWG 1638 | C++11 |
la gramática de declaración de enumeración opaca
prohibía el uso para especializaciones de plantilla |
especificador-de-nombre-anidado
permitido |
| CWG 1766 | C++98 |
convertir un valor fuera de rango a una enumeración
sin tipo subyacente fijo tenía un resultado no especificado |
el comportamiento es indefinido |
| CWG 1966 | C++11 |
la resolución de
CWG issue 1514
hizo que el
:
de una expresión condicional fuera parte de enum-base |
solo aplicar la resolución a
especificadores de declaración de miembro |
| CWG 2156 | C++11 |
las definiciones de enum podían definir
tipos de enumeración mediante using-declarations |
prohibido |
| CWG 2157 | C++11 |
la resolución de
CWG issue 1966
no
cubría nombres de enumeración calificados |
cubierto |
| CWG 2530 | C++98 |
una lista de enumeradores podía contener múltiples
enumeradores con el mismo identificador |
prohibido |
| CWG 2590 | C++98 |
el tamaño, representación de valor y requisitos de alineación
de una enumeración no dependían de su tipo subyacente |
todos son idénticos a
los del tipo subyacente |
| CWG 2621 | C++20 |
la búsqueda de nombres de enumeración utilizada en
using enum declaraciones no estaba clara |
aclarado |
| CWG 2877 | C++20 |
la búsqueda de nombres de enumeración utilizada en
using enum declaraciones no era solo de tipo |
hecho solo de tipo |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 9.7.1 Declaraciones de enumeración [dcl.enum]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 9.7.1 Declaraciones de enumeración [dcl.enum]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 10.2 Declaraciones de enumeración [dcl.enum]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 7.2 Declaraciones de enumeración [dcl.enum]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 7.2 Declaraciones de enumeración [dcl.enum]
- Estándar C++03 (ISO/IEC 14882:2003):
-
- 7.2 Declaraciones de enumeración [dcl.enum]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 7.2 Declaraciones de enumeración [dcl.enum]
Véase también
|
(C++11)
|
verifica si un tipo es un tipo de enumeración
(class template) |
|
(C++23)
|
verifica si un tipo es un tipo de enumeración con ámbito
(class template) |
|
(C++11)
|
obtiene el tipo entero subyacente para un tipo de enumeración dado
(class template) |
|
(C++23)
|
convierte una enumeración a su tipo subyacente
(function template) |
|
Documentación de C
para
Enumeraciones
|
|