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-list
opt}
enum-head
{
enumerator-list
,
}
enum-head
:
enum-key
attribute-specifier-seq
optenum-head-name
optenum-base
opt
enum-head-name
:
nested-name-specifier
optidentifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
optenum-head-name
enum-base
opt;
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
opt
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 Suit
sin á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
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de