Enumerações (C++)
Uma enumeração é um tipo definido pelo usuário que consiste em um conjunto de constantes integrais nomeadas que são conhecidas como enumeradores.
Observação
Este artigo abrange o tipo enum
da Linguagem C++ Padrão ISO e o tipo enum class
com escopo (ou fortemente tipado) que foi introduzido no C++11. Para obter informações sobre os tipos public enum class
ou private enum class
em C++/CLI e C++/CX, confira enum class
(C++/CLI e C++/CX).
Sintaxe
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
O nome do tipo dado à enumeração.
type
O tipo subjacente dos enumeradores; todos os enumeradores têm o mesmo tipo subjacente. Pode ser qualquer tipo integral.
enum-list
Uma lista separada por vírgulas dos enumeradores na enumeração. Cada enumerador ou nome de variável no escopo deve ser exclusivo. No entanto, os valores podem ser duplicados. Em uma enumeração sem escopo, o escopo será o escopo adjacente; em um enum com escopo, o escopo será o próprio enum-list
. Em uma enumeração com escopo, a lista pode estar vazia, o que de fato define um novo tipo integral.
class
Ao usar essa palavra-chave na declaração, você especifica que o enum possui escopo e que um identifier
deve ser fornecido. Você também pode usar a palavra-chave struct
no lugar de class
, porque elas são equivalentes semanticamente nesse contexto.
Escopo do enumerador
Uma enumeração fornece o contexto para descrever um intervalo de valores que são representados como constantes nomeadas. Essas constantes nomeadas também são chamadas de enumeradores. Nos tipos enum
originais de C e C++, os enumeradores não qualificados são visíveis em todo o escopo no qual enum
é declarado. Em enums com escopo, o nome do enumerador deve ser qualificado pelo nome do tipo enum
. O exemplo a seguir demonstra essa diferença básica entre os dois tipos de enums:
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
{ /*...*/
}
}
}
Cada nome em uma enumeração recebe um valor integral que corresponde ao seu local na ordem dos valores na enumeração. Por padrão, o primeiro valor é atribuído a 0, o seguinte é atribuído a 1, e assim por diante, mas você pode definir explicitamente o valor de um enumerador, como mostrado aqui:
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
O enumerador Diamonds
recebe o valor 1
. Os enumeradores subsequentes, se não recebem um valor explícito, recebem o valor do enumerador anterior mais um. No exemplo anterior, Hearts
teria o valor 2, Clubs
teria 3 e assim por diante.
Cada enumerador é tratado como uma constante e deve ter um nome exclusivo no escopo onde enum
é definido (para enums sem escopo) ou dentro do enum
em si (para enums com escopo). Os valores dados para os nomes não devem ser exclusivos. Por exemplo, considere esta declaração de uma enumeração sem escopo Suit
:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Os valores de Diamonds
, Hearts
, Clubs
e Spades
são 5, 6, 4 e 5, respectivamente. Observe que 5 será usado mais de uma vez; isso é permitido mesmo que não seja pretendido. Essas regras são as mesmas para enums com escopo.
Regras de conversão
As constantes enum sem escopo podem ser convertidas implicitamente em int
, mas um int
nunca pode ser convertido implicitamente em um valor enum. O exemplo a seguir mostra o que acontece se você tentar atribuir hand
a um valor que não seja um Suit
:
int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
Uma conversão é necessária para converter um int
em um enumerador com escopo ou sem escopo. No entanto, você pode promover um enumerador para um valor inteiro sem uma conversão.
int account_num = Hearts; //OK if Hearts is in an unscoped enum
O uso de conversões implícitas dessa maneira pode levar a efeitos colaterais não intencionais. Para ajudar a eliminar erros de programação associados aos enums sem escopo, os valores enum com escopo são fortemente tipados. Os enumeradores com escopo devem ser qualificados pelo nome do tipo enum (identificador) e não podem ser convertidos implicitamente, conforme mostrado no exemplo a seguir:
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 a linha hand = account_num;
ainda causa o erro que ocorre com enums sem escopo, como mostrado anteriormente. É permitido com uma conversão explícita. No entanto, com enums com escopo, a conversão tentada na próxima instrução, account_num = Suit::Hearts;
, não é mais permitida sem uma conversão explícita.
Enums sem enumeradores
Visual Studio 2017 versão 15.3 e posterior (disponível com /std:c++17
e posterior): definindo uma enumeração (regular ou com escopo) com um tipo subjacente explícito e sem enumeradores, você pode introduzir um novo tipo integral que não tenha nenhuma conversão implícita para qualquer outro tipo. Usando esse tipo em vez de seu tipo subjacente interno, você pode eliminar o potencial de erros sutis causados por conversões implícitas inadvertidas.
enum class byte : unsigned char { };
O novo tipo é uma cópia exata do tipo subjacente e, portanto, tem a mesma convenção de chamada, o que significa que ele pode ser usado entre ABIs sem nenhuma penalidade de desempenho. Nenhuma conversão é necessária quando variáveis do tipo são inicializadas usando a inicialização de lista direta. O exemplo a seguir mostra como inicializar enumeradores sem enumeradores em vários 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;
}
Confira também
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de