Menu

Enums em C++: enum class vs enum comum explicado

Aprenda os enums de C++: como declará-los, por que o enum class com escopo é mais seguro que o enum comum, valores subjacentes personalizados, usar switch com enumeradores e converter de e para inteiros.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Para que serve um enum

Uma struct agrupa vários valores relacionados em um único objeto. Um enum resolve um problema diferente: ele dá nomes a um conjunto pequeno e fixo de opções. Em vez de lembrar que 0 significa "vermelho", 1 significa "verde" e 2 significa "azul", você escreve Color::Red e o compilador te mantém honesto.

Recorrer a um enum sempre que uma variável só pode assumir um de um punhado de estados nomeados — a cor de um semáforo, o naipe de uma carta, um status de conexão — torna o código autoexplicativo e permite que o compilador pegue erros de digitação e casos faltantes que inteiros puros nunca pegariam.

Declarando um enum

O C++ moderno tem duas variantes. Comece pela que você deve usar quase sempre: o enum class com escopo. Você lista os nomes, e cada enumerador é acessado pelo nome do enum com :::

Repare que você escreve Color::Green, nunca um Green solto. Os valores em si são apenas rótulos: você os compara, atribui e passa adiante, mas raramente se importa com o número subjacente. Por padrão Red é 0, Green é 1 e Blue é 2, contando a partir de zero.

enum comum vs enum class

O enum mais antigo e sem escopo (sem a palavra-chave class) despeja seus nomes diretamente no escopo ao redor e se converte para int sozinho. Isso parece conveniente, mas causa dois problemas reais:

enum Color { Red, Green, Blue };
enum Fruit { Apple, Banana, Red };  // error: 'Red' already declared

enum Status { Active, Inactive };
int x = Active;          // compiles silently - is this what you meant?
if (Active == Banana) {  // compares unrelated enums via int - allowed!
}

Como os enumeradores comuns são nomes globais, dois enums podem colidir só por compartilharem um rótulo. E como eles decaem para int, o compilador compara tranquilamente valores de enums totalmente sem relação. Um enum class com escopo corrige as duas coisas: os nomes vivem dentro do tipo, e o tipo não vai se transformar silenciosamente em um int:

A regra prática: use enum class por padrão. Só recorra a um enum comum quando você quiser especificamente a conversão implícita para int, como nas antigas constantes de flags no estilo C.

Valores personalizados e o tipo subjacente

Você pode atribuir números explícitos aos enumeradores. Os que você deixar de fora continuam contando a partir do anterior, o que é prático para coisas como códigos de status HTTP ou flags de bits:

Todo enum é respaldado por um tipo inteiro — int por padrão. Você pode fixá-lo em um tipo menor quando se importa com o tamanho, por exemplo ao armazenar muitos enums em uma estrutura compacta ou ao corresponder a um formato de transmissão:

Escolher o tipo subjacente também garante a faixa em que seus valores devem caber: um enum uint8_t não pode conter um valor acima de 255.

Convertendo entre enum e int

Um enum class com escopo nunca se converte implicitamente, e esse é justamente o objetivo. Quando você realmente precisa do número — para imprimi-lo, indexar um array ou lê-lo de um arquivo — recorra ao static_cast. Ir de enum para int é sempre seguro:

Converter um int de volta para um enum é a direção perigosa. O cast não verifica que o número corresponda a um enumerador real: ele vai te entregar um valor que tecnicamente está fora do conjunto de nomes do enum:

Suit s = static_cast<Suit>(2);   // fine - that's Clubs
Suit bad = static_cast<Suit>(99); // compiles, but 99 is not a valid Suit
// using `bad` in a switch or as an array index is a lurking bug

Se o inteiro vem da entrada do usuário ou de um arquivo, valide você mesmo a faixa antes de fazer o cast, ou você criará um valor que nenhum case jamais trata — uma fonte sutil de comportamento indefinido mais adiante.

Usando enums com switch

Como um enum é "um de um conjunto fixo", ele combina perfeitamente com um switch. Quando você cobre todos os enumeradores, muitos compiladores vão avisar se mais tarde você adicionar um valor novo e esquecer de tratá-lo: uma segurança de graça que você não tem com inteiros puros:

Uma pegadinha: não há uma forma nativa de imprimir o nome de um enumerador. cout << TrafficLight::Red não vai compilar para um enum com escopo, e mesmo para um enum comum ele imprime o número, não "Red". Um pequeno switch ou uma tabela de consulta como a acima é a forma usual de transformar um enum em uma string legível para humanos.

Próximo: Exceções

Enums e structs permitem modelar como seus dados se parecem. Mas programas reais também precisam lidar com coisas dando errado — um arquivo que não abre, um número que não dá para parsear, um valor fora da faixa. C++ trata esses caminhos de falha com exceções, e esse é o tema da próxima página.

Perguntas frequentes

Qual é a diferença entre enum e enum class em C++?

Um enum comum vaza seus nomes para o escopo ao redor e se converte implicitamente para int, o que causa conflitos de nomes e comparações acidentais. Um enum class com escopo mantém seus nomes dentro do enum (Color::Red) e se recusa a converter para int sem um cast explícito. Prefira enum class no C++ moderno: ele é seguro quanto a tipos e evita as pegadinhas clássicas.

Como converter um enum de C++ para um int?

Um enum comum se converte implicitamente, então int n = Red; simplesmente funciona. Um enum class com escopo exige um cast explícito: int n = static_cast<int>(Color::Red);. Para fazer o caminho inverso, faça o cast do int de volta: Color c = static_cast<Color>(2);, mas cuidado: em tempo de execução não há verificação de que o valor seja um enumerador válido.

Em que valor o primeiro enum de C++ começa?

Por padrão o primeiro enumerador é 0, e cada um seguinte vale um a mais que o anterior. Então em enum class Level { Low, Mid, High };, Low é 0, Mid é 1 e High é 2. Você pode atribuir valores explícitos a qualquer um deles para sobrescrever esse comportamento.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR