Espacios de nombres
Variantes
Acciones

Declaración de enumeración

De cppreference.com
< cpp‎ | language
 
 
 
 

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)
1) El especificador-enum, que aparece en la sec-decl-especificadores de la sintaxis de declaración: define el tipo enumeración y sus enumeradores.
2) Declaración de enumeración opaca: define el tipo enumeración pero no sus enumeradores: después de esta declaración, el tipo es un tipo completo y se conoce su tamaño. Nota: una declaración de especialización explícita de un miembro de una enumeración con ámbito de una plantilla de clase es el único caso donde el especificador de nombre anidado aparece antes del nombre-de-enum (desde C++14)
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)
1) Declara un tipo enumeración sin ámbito cuyo tipo subyacente no es fijo (en este caso, el tipo subyacente es un tipo entero definido por la implementación que puede representar todos los valores del enumerador; este tipo no es mayor que 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).
2) Declara un tipo enumeración sin ámbito cuyo tipo subyacente es fijo.
3) La declaración de enumeración opaca para una enumeración sin ámbito debe especificar el tipo subyacente.

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.

enum Color { rojo, verde, azul };
Color r = rojo;
switch(r)
{
    case rojo  : std::cout << "rojo\n";   break;
    case verde : std::cout << "verde\n"; break;
    case azul  : std::cout << "azul\n";  break;
}

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

enum struct|class nombre { enumerador = constexpr , enumerador = constexpr , ... } (1)
enum struct|class nombre : tipo { enumerador = constexpr , enumerador = constexpr , ... } (2)
enum struct|class nombre ; (3)
enum struct|class nombre : tipo ; (4)
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:

  • la inicialización es inicialización de lista directa;
  • la lista de inicializadores tiene un solo elemento;
  • la enumeración es o bien con ámbito, o no tiene ámbito con el tipo subyacente fijo;
  • la conversión no estrecha.

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

using enum especificador-de-nombre-anidado(opcional) nombre ; (desde C++20)

donde el especificador-de-nombre-anidado(opcional) nombre no debe denominar un tipo dependiente y debe denominar un tipo enumeración.

Una declaración using enum introduce los nombres de los enumeradores de la enumeración denominada como si lo fueran mediante una declaración using para cada enumerador. Cuando está dentro del ámbito de una clase, una declaración using enum agrega los enumeradores a la enumeración denominada como miembros del ámbito, haciéndolos accesibles a la búsqueda de miembros.

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 using enum que introducen dos enumeradores con el mismo nombre entran en conflicto.

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