Compartir a través de


Enumeraciones (C++)

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

Nota:

En este artículo se describe el tipo de lenguaje enum C++ estándar ISO y el tipo de ámbito (o fuertemente tipado) enum class que se introduce en C++11. Para obtener información sobre los tipos o private enum class en C++/CLI y C++/CX, consulte enum class (C++/CLI y C++/CX).public enum class

Sintaxis

enum-name:
identifier

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

enum-head:
enum-key attribute-specifier-seq optarenum-head-nameoptarenum-baseoptar

enum-head-name:
nested-name-specifier optaridentifier

opaque-enum-declaration:
enum-key attribute-specifier-seq optarenum-head-nameenum-baseoptar;

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:
identifier attribute-specifier-seq optar

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 de tipo proporcionado a la enumeración.

type
Tipo subyacente de los enumeradores; todos los enumeradores tienen el mismo tipo subyacente. Puede ser cualquier tipo entero.

enum-list
Lista separada por comas de los enumeradores de la enumeración. Cada enumerador o nombre de variable del ámbito debe ser único. Sin embargo, los valores se pueden duplicar. En una enumeración sin ámbito, el ámbito es el ámbito circundante; en una enumeración con ámbito, el ámbito es el enum-list propio . 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, se especifica el ámbito de la enumeración y se debe proporcionar .identifier También puede usar la struct palabra clave en lugar de class, ya que son semánticamente equivalentes en este contexto.

Ámbito del enumerador

Una enumeración proporciona contexto para describir un intervalo de valores representados como constantes con nombre. Estas constantes con nombre también se denominan enumeradores. En los tipos originales de C y C++enum, los enumeradores no calificados son visibles en todo el ámbito en el que se declara .enum En las enumeraciones con ámbito, el nombre del enumerador debe estar calificado por el nombre de enum tipo. En el ejemplo siguiente se muestra esta diferencia básica entre los dos tipos 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 una enumeración se le asigna un valor entero que corresponde a su lugar en el orden de los valores de la enumeración. De forma predeterminada, el primer valor se asigna a 0, al siguiente se le asigna 1, etc., pero puede establecer explícitamente el valor de un enumerador, como se muestra aquí:

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

Al enumerador Diamonds se le asigna el valor 1. Los enumeradores posteriores, 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 se define (para enumeraciones sin ámbito) o dentro de la enum propia (para enumeraciones con ámbito). Los valores proporcionados a 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, Clubsy Spades son 5, 6, 4 y 5, respectivamente. Observe que se usa 5 más de una vez; se permite aunque no esté previsto. Estas reglas son las mismas para las enumeraciones con ámbito.

Reglas de conversión

Las constantes de enumeración sin ámbito se pueden convertir implícitamente en int, pero int nunca se puede convertir implícitamente en un valor de enumeración. En el ejemplo siguiente se muestra lo que sucede si intenta asignar hand un valor que no es un 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 objeto en un enumerador con ámbito o sin ámbito. Sin embargo, 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

El uso de conversiones implícitas de esta manera puede provocar efectos secundarios no deseados. Para ayudar a eliminar los errores de programación asociados a enumeraciones sin ámbito, los valores de enumeración con ámbito están fuertemente tipados. Los enumeradores con ámbito deben estar calificados por el nombre de tipo de enumeración (identificador) y no se pueden convertir 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; sigue provocando el error que se produce con enumeraciones sin ámbito, como se muestra anteriormente. Se permite con una conversión explícita. Sin embargo, con enumeraciones con ámbito, la conversión intentada 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 ningún enumerador, 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 el potencial 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 API sin ninguna penalización de rendimiento. No se requiere ninguna conversión cuando las variables del tipo se inicializan mediante la inicialización de la 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