Enumeraciones (C++)

Una enumeración es un tipo definido por el usuario que consta de un conjunto de constantes enteras con nombre conocidas como enumeradores.

Nota:

Este artículo trata el tipo enum del lenguaje C++ estándar de ISO y el tipo de ámbito (o fuertemente tipado) enum class, que se introdujo en C++11. Para obtener información acerca de los tipos public enum class o private enum class en C++/CLI y C++/CX, vea enum class (C++/CLI y C++/CX).

Sintaxis

enum-name:
identifier

enum-specifier:
enum-head{enumerator-listopt}
enum-head { enumerator-list , }

enum-head:
enum-keyattribute-specifier-seqoptenum-head-nameoptenum-baseopt

enum-head-name:
nested-name-specifieroptidentifier

opaque-enum-declaration:
enum-keyattribute-specifier-seqoptenum-head-nameenum-baseopt;

enum-key:
enum
enum class
enum struct

enum-base:
: type-specifier-seq

enumerator-list:
enumerator-definition
enumerator-list , enumerator-definition

enumerator-definition:
enumerator
enumerator = constant-expression

enumerator:
identifierattribute-specifier-seqopt

Uso

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

Parámetros

identifier
Nombre del tipo dado a la enumeración.

type
El tipo subyacente de los enumeradores; cada enumerador tiene el mismo tipo subyacente. Puede ser cualquier tipo entero.

enum-list
Una lista delimitada por comas de los enumeradores en la enumeración. Cada enumerador o nombre de variable en el ámbito debe ser único. Sin embargo, los valores pueden estar duplicados. En una enumeración sin ámbito, el ámbito es el ámbito adyacente; en una enumeración con ámbito, el ámbito es el mismo elemento enum-list. En una enumeración con ámbito, la lista puede estar vacía, que en efecto define un nuevo tipo entero.

class
Al usar esta palabra clave en la declaración, especifica que la enumeración se incluye en el ámbito, por lo que es necesario proporcionar identifier. También puede usar la palabra clave struct en lugar de class, ya que son equivalentes semánticas en este contexto.

Ámbito del enumerador

Una enumeración proporciona contexto para describir un intervalo de valores que se representan como constantes con nombre. Estas constantes con nombre también se denominan enumeradores. En los tipos enum originales de C y C++, los enumeradores incompletos están visibles en el ámbito en el que se declara enum. En enumeraciones de ámbito, el nombre del enumerador debe calificarse con el nombre de tipo enum. El ejemplo siguiente muestra esta diferencia básica entre las dos clases de enumeraciones:

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

A cada nombre de la enumeración se le asigna un valor entero que corresponde al lugar que ocupa en el orden de los valores de la enumeración. De forma predeterminada, al primer valor se asigna 0, al siguiente se asigna 1 y así sucesivamente, pero puede establecer explícitamente el valor de un enumerador, como se muestra aquí:

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

El enumerador Diamonds tiene asignado el valor 1. Los enumeradores subsiguientes, si no se les asigna un valor explícito, reciben el valor del enumerador anterior más uno. En el ejemplo anterior, Hearts tendría el valor 2, Clubs tendría 3, etc.

Cada enumerador se trata como una constante y debe tener un nombre único dentro del ámbito, donde enum está definido (para las enumeraciones sin ámbito) o en el propio elemento enum (para las enumeraciones de ámbito). Los valores especificados en los nombres no tienen que ser únicos. Por ejemplo, considere esta declaración de una enumeración Suitsin ámbito:

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

Los valores de Diamonds, Hearts, Clubs y Spades son 5, 6, 4 y 5, respectivamente. Observe que 5 se usa más de una vez; esto se permite incluso aunque pueda no ser intencionado. Estas reglas son las mismas para las enumeraciones de ámbito.

Reglas de conversión

Las constantes de enumeración sin ámbito se pueden convertir implícitamente a int, pero int nunca es implícitamente convertible a un valor enum. El ejemplo siguiente muestra lo que ocurre si intenta asignar a hand un valor que no sea Suit:

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

Se requiere una conversión para convertir un int a un enumerador con ámbito o sin ámbito. Pero puede promover un enumerador sin ámbito a un valor entero sin una conversión.

int account_num = Hearts; //OK if Hearts is in an unscoped enum

Utilizar conversiones implícitas de esta manera puede provocar efectos secundarios imprevistos. Para ayudar a eliminar los errores de programación asociados a las enumeraciones sin ámbito, los valores de ámbito de enumeración están fuertemente tipados. Los enumeradores con ámbito deben calificarse por el nombre de tipo de enumeración (identificador) y no pueden convertirse implícitamente, como se muestra en el ejemplo siguiente:

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

Observe que la línea hand = account_num; aún produce el error que se produce con enumeraciones sin ámbito, como se muestra anteriormente. Esto se permite con una conversión explícita. Sin embargo, con las enumeraciones con ámbito, la conversión que se ha intentado en la siguiente instrucción, account_num = Suit::Hearts;, ya no se permite sin una conversión explícita.

Enumeraciones sin enumeradores

Visual Studio 2017 versión 15.3 y posteriores (disponible con /std:c++17 y versiones posteriores): al definir una enumeración (normal o con ámbito) con un tipo subyacente explícito y sin enumeradores, puede introducir un nuevo tipo entero que no tenga ninguna conversión implícita a ningún otro tipo. Mediante el uso de este tipo en lugar de su tipo subyacente integrado, puede eliminar la posibilidad de errores sutiles causados por conversiones implícitas involuntarias.

enum class byte : unsigned char { };

El nuevo tipo es una copia exacta del tipo subyacente y, por tanto, tiene la misma convención de llamada, lo que significa que se puede usar en las ABI sin ninguna penalización de rendimiento. No se requiere ninguna conversión cuando se inicializan variables del tipo mediante la inicialización de lista directa. En el ejemplo siguiente se muestra cómo inicializar enumeraciones sin enumeradores en varios contextos:

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

Consulte también

Declaraciones de enumeración de C
Palabras clave