Declaración de enumeración
Una enumeración es un tipo distinto cuyo valor está restringido a un rango de valores (véase más abajo para más detalles), que puede incluir varias constantes nombradas explícitamente ("enumeradores"). Los valores de las constantes son valores de un tipo integral conocido como el tipo subyacente de la enumeración.
Una enumeración se define utilizando la siguiente sintaxis:
clave-de-enum atrib(opcional) nombre-de-enum(opcional) base-de-enum(opcional)(C++11) { lista-de-enumeradores(opcional) }
|
(1) | ||||||||
clave-de-enum atrib(opcional) nombre-de-enum base-de-enum(opcional) ;
|
(2) | (desde C++11) | |||||||
clave-de-enum | - | Uno de enum , enum class (desde C++11), o enum struct (desde C++11)
|
atrib(C++11) | - | Secuencia opcional de cualquier número de atributos |
nombre-de-enum | - | El nombre de la enumeración que se declara. Si está presente, y si esta declaración es una nueva declaración, puede estar precedida por el especificador de nombre anidado(desde C++11): secuencia de nombres y operadores de resolución de ámbito :: , que termina con el operador de resolución de ámbito. El nombre solo se puede omitir en las declaraciones de enumeración sin ámbito.
|
base-de-enum(C++11) | - | Dos puntos (: ), seguido de una sec-especificadores-de-tipo que denominan un tipo entero (si está calificado-cv, se ignoran las calificaciones) que servirá como el tipo subyacente fijo para este tipo enumeración
|
lista-de-enumeradores | - | Lista de definiciones de enumeradores separadas por comas, cada una de las cuales es simplemente un identificador, que se convierte en el nombre del enumerador, o un identificador con un inicializador: identificador = constexpr. En cualquier caso, el identificador puede estar seguido directamente por una secuencia de especificadores de atributos. (desde C++17)
|
Hay dos tipos distintos de enumeraciones: enumeración sin ámbito (declarada con la clave-de-enum enum
) y enumeración con ámbito (declarada con la clave-de-enum enum class
o enum struct
).
Contenido |
[editar] Enumeración sin ámbito
enum nombre { enumerador = constexpr , enumerador = constexpr , ... }
|
(1) | ||||||||
enum nombre : type { enumerador = constexpr , enumerador = constexpr , ... }
|
(2) | (desde C++11) | |||||||
enum nombre : type ;
|
(3) | (desde C++11) | |||||||
int
a menos que el valor de un enumerador no pueda ajustarse en un int
o un unsigned int
}. Si la lista de enumeradores está vacía, el tipo subyacente es como si la enumeración tuviera un único enumerador con valor 0).Cada enumerador se convierte en una constante con nombre del tipo enumeración (es decir, nombre), visible en el ámbito circundante, y puede usarse siempre que se requieran constantes.
Cada enumerador está asociado con un valor del tipo subyacente. Cuando los inicializadores se proporcionan en la lista-de-enumeradores, los valores de los enumeradores están definidos por esos inicializadores. Si el primer enumerador no tiene un inicializador, el valor asociado es cero. Para cualquier otro enumerador cuya definición no tenga un inicializador, 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
Los valores del tipo enumeración sin ámbito son implícitamente convertibles a tipos enteros. Si el tipo subyacente no es fijo, el valor es convertible al primer tipo de la siguiente lista capaz de mantener todo su rango de valores: int, unsigned int, long, unsigned long, long long, o unsigned long long. Si el tipo subyacente es fijo, los valores se pueden convertir a su tipo subyacente promocionado.
enum color { rojo, amarillo, verde = 20, azul }; color col = rojo; int n = azul; // n == 21
Los valores de tipos enteros, de punto flotante y enumeración se pueden convertir mediante una conversión estática (static_cast
) o una conversión explícita, a cualquier tipo enumeración. Si el tipo subyacente no es fijo y el valor de origen está fuera de rango, el resultado no está especificado (hasta C++17) está indefinido (desde C++17). (El valor de origen, como se convierte al tipo subyacente de la enumeración si es de punto flotante, está dentro del rango si se ajusta en el campo de bits más pequeño lo suficientemente grande para contener todos los enumeradores de la enumeración destino). De lo contrario, el resultado es el mismo que el resultado de la conversión explícita al tipo subyacente.
Observa que el valor después de dicha conversión puede no ser necesariamente igual a ninguno de los enumeradores definidos por la enumeración.
enum acceso_t { lectura = 1, escritura = 2, ejecucion = 4 }; // enumeradores: 1, 2, 4 rango: 0..7 acceso_t lej = static_cast<acceso_t>(7); assert((lej & lectura) && (lej & escritura) && (lej & ejecucion)); acceso_t x = static_cast<acceso_t>(8.0); // comportamiento indefinido a partir de C++17 acceso_t y = static_cast<acceso_t>(8); // comportamiento indefinido a partir de C++17 enum foo { a = 0, b = UINT_MAX }; // rango: [0, UINT_MAX] foo x= foo(-1); // comportamiento indefinido a partir de C++17, // aún si el tipo subyacente de foo es unsigned int
Se puede omitir el nombre de una enumeración sin ámbito: dicha declaración solo introduce a 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 sin ámbito es un miembro de clase, se puede acceder a sus enumeradores utilizando operadores de acceso a miembros de la clase .
and ->
:
struct X { enum direccion { izquierda = 'i', derecha = 'd' }; }; X x; X* p = &x; int a = X::direccion::left; // permitido solamente en C++11 y posterior int b = X::izquierda; int c = x.izquierda; int d = p->izquierda;
[editar] Enumeraciones con ámbito
1) Declara un tipo enumeración con ámbito cuyo tipo subyacente es int (las palabras clave class y struct son exactamente equivalentes).
2) Declara un tipo 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 enumeración (es decir, nombre), que está contenida dentro del ámbito de la enumeración, y se puede acceder mediante el operador de resolución de ámbito. No hay conversiones implícitas de los valores de un enumerador con ámbito a tipos integrales, aunque se puede utilizar static_cast para obtener el valor numérico del enumerador. enum class Color { rojo, verde = 20, azul }; Color r = Color::azul; switch(r) { case Color::rojo : std::cout << "rojo\n"; break; case Color::verde : std::cout << "verde\n"; break; case Color::azul : std::cout << "azul\n"; break; } // int n = r; // ERROR: no existe conversión de enum con ámbito a int int n = static_cast<int>(r); // de acuerdo, n = 21 |
(desde C++11) |
Tanto los tipos enumeración con ámbito como los tipos enumeración sin ámbito cuyo tipo subyacente es fijo pueden inicializarse desde un entero sin conversión, utilizando la inicialización de lista, si todo lo siguiente es verdadero:
Esto hace posible introducir nuevos tipos enteros (por ejemplo, SafeInt) que disfrutan de las mismas convenciones de llamada existentes que sus tipos enteros subyacentes, incluso en las ABI que penalizan las estructuras el paso/retorno por valor. enum byte : unsigned char {}; // byte es un nuevo tipo de entero byte b { 42 }; // de acuerdo a partir de C++17 (inicialización de lista directa) byte c = { 42 }; // ERROR byte d = byte{ 42 }; // de acuerdo a partir de C++17; mismo valor que b byte e { -1 }; // ERROR struct A { byte b; }; A a1 = { { 42 } }; // ERROR A a2 = { byte{ 42 } }; // de acuerdo a partir de C++17 void f(byte); f({ 42 }); // ERROR enum class Identificador : std::uint32_t { Invalido = 0 }; Identificador h { 42 }; // de acuerdo a partir de C++17 |
(desde C++17) |
Declaración using enum
donde el especificador-de-nombre-anidado(opcional) nombre no debe denominar un tipo dependiente y debe denominar un tipo enumeración. Una declaración enum class fruta { naranja, manzana }; struct S { using enum fruta; // de acuerdo: introduce a naranja y manzana en S }; void f() { S s; s.naranja; // de acuerdo: denomina a fruta::naranja S::naranja; // de acuerdo: denomina a fruta::naranja } Dos declaraciones enum class fruta { naranja, manzana }; enum class color { rojo, naranja }; void f() { using enum fruta; // de acuerdo // using enum color; // ERROR: color::naranja y fruta::orange en conflicto } |
(desde C++20) |
[editar] Ejemplo
#include <iostream> // enum que toma 16 bits enum smallenum: int16_t { a, b, c }; // color puede ser rojo (valor 0), amarillo (valor 1), verde (valor 20), o azul (valor 21) enum color { rojo, amarillo, verde = 20, azul }; // altitud puede ser altitud::alta o altitud::baja enum class altitud: char { alta = 'a', baja = 'b', // C++11 permite la coma extra }; // la constante d es 0, la constante e es 1, la constante f es 3 enum { d, e, f = e + 2 }; // tipos enumeración (tanto con ámbito como sin ámbito) // pueden tener operadores sobrecargados std::ostream& operator<<(std::ostream& os, color c) { switch(c) { case rojo : os << "rojo"; break; case amarillo : os << "amarillo"; break; case verde : os << "verde"; break; case azul : os << "azul"; break; default : os.setstate(std::ios_base::failbit); } return os; } std::ostream& operator<<(std::ostream& os, altitude al) { return os << static_cast<char>(al); } int main() { color col = rojo; altitud a; a = altitud::baja; std::cout << "col = " << col << '\n' << "a = " << a << '\n' << "f = " << f << '\n'; }
Salida:
col = rojo a = b f = 3
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 1638 | C++14 | La gramática de la declaración de enumeración opaca prohibió el uso para especializaciones de plantillas. |
Se permite el especificador de nombre anidado. |
[editar] Véase también
Documentación de C para Enumeraciones
|