Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
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
Special member functions
Templates
Miscellaneous

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)
1) enum-specifier , que aparece en decl-specifier-seq de la declaración sintaxis: define el tipo de enumeración y sus enumeradores.
2) Una coma final puede seguir a la enumerator-list .
3) Declaración de enumeración opaca: define el tipo de enumeración pero no sus enumeradores: después de esta declaración, el tipo es un tipo completo y se conoce su tamaño.
enum-key -

enum

(hasta C++11)

uno de enum , enum class , o enum struct

(desde C++11)
attr - (desde C++11) secuencia opcional de cualquier número de atributos
enum-head-name -

el nombre de la enumeración que se está declarando, puede omitirse.

(hasta C++11)

el nombre de la enumeración que se está declarando, opcionalmente precedido por un especificador-de-nombre-anidado : secuencia de nombres y operadores de resolución de ámbito :: , terminando con el operador de resolución de ámbito. Solo puede omitirse en declaraciones de enumeraciones no acotadas no opacas.
especificador-de-nombre-anidado solo puede aparecer si el nombre de la enumeración está presente y esta declaración es una redeclaración. Para declaraciones de enumeraciones opacas, especificador-de-nombre-anidado solo puede aparecer antes del nombre de la enumeración en declaraciones de especialización explícita .
Si especificador-de-nombre-anidado está presente, el especificador-de-enum no puede referirse a una enumeración meramente heredada o introducida por una using declaración , y el especificador-de-enum solo puede aparecer en un espacio de nombres que englobe la declaración anterior. En tales casos, especificador-de-nombre-anidado no puede comenzar con un especificador decltype .

(desde C++11)
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)
1) Declara un tipo de enumeración no delimitado cuyo tipo subyacente no está fijo (en este caso, el tipo subyacente es un tipo integral definido por la implementación que puede representar todos los valores de los enumeradores; este tipo no es mayor que int a menos que el valor de un enumerador no pueda caber en un int o unsigned int . Si la enumerator-list está vacía, el tipo subyacente es como si la enumeración tuviera un único enumerador con valor 0 . Si ningún tipo integral puede representar todos los valores de los enumeradores, la enumeración está mal formada).
2) Declara un tipo de enumeración no delimitado cuyo tipo subyacente está fijo.
3) La declaración de enumeración opaca para una enumeración no delimitada debe especificar el nombre y el tipo subyacente.

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.

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

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

enum enum-head-name :

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

enum struct|class nombre { enumerador = expresión-constante , enumerador = expresión-constante , ... } (1)
enum struct|class nombre : tipo { enumerador = expresión-constante , enumerador = expresión-constante , ... } (2)
enum struct|class nombre ; (3)
enum struct|class nombre : tipo ; (4)
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 static_cast para obtener el valor numérico del enumerador.

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

  • La inicialización es de inicialización de lista directa.
  • La lista de inicializadores tiene solo un elemento.
  • La enumeración es con ámbito (scoped) o sin ámbito con tipo subyacente fijo.
  • La conversión no es de estrechamiento (non-narrowing).

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 ABIs que penalizan pasar/devolver estructuras por valor.

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

using enum declarador-using-enum ; (desde C++20)
declarador - un (posiblemente calificado) identificador o identificadores de plantilla simples


declarador debe nombrar un tipo de enumeración no dependiente . Las declaraciones de enumeración se encuentran mediante búsqueda calificada o no calificada ordinaria solo por tipo, dependiendo de si declarador está calificado.

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

enum , struct , class , using

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)
verifica si un tipo es un tipo de enumeración con ámbito
(class template)
obtiene el tipo entero subyacente para un tipo de enumeración dado
(class template)
convierte una enumeración a su tipo subyacente
(function template)
Documentación de C para Enumeraciones