Por que enum class é seu novo melhor amigo em C++?
Todo mundo que começou a estudar C++ ou voltou a ele após uma pausa, mais cedo ou mais tarde, enfrenta o dilema: usar o bom e velho enum ou seu irmão mais moderno enum class? No Stack Overflow, a pergunta «Why is enum class considered safer to use than plain enum?» acumulou milhares de visualizações — e não é por acaso. A migração para scoped enum (que é o nome oficial do enum class) não é apenas uma questão de moda, mas um passo real em direção a um código mais confiável e legível.
Vamos entender exatamente onde está o perigo do enum antigo e como o enum class ajuda a evitar toda uma classe de erros. Se você programa em C++ e ainda usa enum sem escopo, este artigo é para você.
Top 3 erros do enum antigo (e como enum class os corrige)
1️⃣ Poluição do escopo global
O problema mais comum do enum antigo é que suas constantes "vazam" para o espaço de nomes ao redor. Imagine que você tem duas enumerações:
// Ruim: enum antigoenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // Erro de compilação! Blue já foi definidoA constante Blue de Color já está ocupada, e você não pode usar o mesmo nome em Feeling. Em um projeto grande com centenas de arquivos, isso causa uma verdadeira dor de cabeça. Você precisa inventar nomes longos como Color_Blue ou COLOR_BLUE, o que polui o código.
Solução com enum class:
// Bom: enum class (scoped enum)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // OK! Sem conflitoAgora Blue vive dentro de sua própria classe. Para acessá-lo, você precisa escrever Color::Blue ou Feeling::Blue. Isso não apenas resolve o problema de colisões, mas também torna o código autodocumentado — fica imediatamente claro a qual tipo o valor pertence.
2️⃣ Conversão implícita para int (e vice-versa)
O enum antigo é convertido implicitamente para tipos inteiros. À primeira vista, isso é conveniente, mas na prática é uma fonte de bugs difíceis de detectar.
// Enum antigo perigosoenum WarningLevel { Low, Medium, High };
void setVolume(int level) { if (level > 10) level = 10; // ...}
setVolume(High); // Funciona! Mas High = 2, não é um nível de volume// Você acidentalmente passou um nível de alerta em vez do volume — o compilador não reclamaO compilador aceita isso sem um único aviso. E se você trocar a ordem dos argumentos em uma função — o bug vai para o runtime.
Como enum class protege:
enum class WarningLevel { Low, Medium, High };
void setVolume(int level);
setVolume(WarningLevel::High); // Erro de compilação!// Não há conversão implícita de enum class para intO compilador força você a converter o valor explicitamente, se realmente necessário. Isso significa que você tem consciência de cada conversão, em vez de confiar no "talvez funcione".
// Se for realmente necessário — faça explicitamentesetVolume(static_cast<int>(WarningLevel::High)); // Explícito e claro3️⃣ Tipo base indefinido
O enum antigo não garante qual tipo inteiro será usado internamente. O compilador escolhe por conta própria (geralmente int), e isso pode causar surpresas ao transferir código entre diferentes compiladores ou plataformas.
// O tamanho pode ser diferente!enum Small { A, B, C }; // Pode ser int, pode ser charenum Big { X = 100000, Y, Z }; // Ainda pode ser intCom enum class, você pode especificar explicitamente o tipo base, o que dá controle total