¿Por qué enum class es su nuevo mejor amigo en C++?
Cualquiera que haya empezado a estudiar C++ o haya vuelto a él después de un descanso, tarde o temprano se enfrenta al dilema: usar el clásico enum o su hermano más moderno enum class? En Stack Overflow, la pregunta «Why is enum class considered safer to use than plain enum?» ha acumulado miles de visitas, y no es casualidad. La transición a scoped enum (así se llama oficialmente enum class) no es solo una cuestión de moda, sino un paso real hacia un código más fiable y legible.
Analicemos en qué consiste exactamente el peligro del antiguo enum y cómo enum class ayuda a evitar toda una clase de errores. Si programas en C++ y todavía usas enum sin ámbito de visibilidad, este artículo es para ti.
Top 3 errores del antiguo enum (y cómo los corrige enum class)
1️⃣ Contaminación del ámbito global de visibilidad
El problema más común del antiguo enum es que sus constantes «se salen» al espacio de nombres circundante. Imagina que tienes dos enumeraciones:
// Mal: antiguo enumenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // ¡Error de compilación! Blue ya está definidoLa constante Blue de Color ya está ocupada, y no puedes usar el mismo nombre en Feeling. En un proyecto grande con cientos de archivos, esto se convierte en un verdadero dolor de cabeza. Hay que inventar nombres largos como Color_Blue o COLOR_BLUE, lo que afea el código.
Solución con enum class:
// Bien: enum class (scoped enum)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // ¡OK! No hay conflictoAhora Blue vive dentro de su propia clase. Para acceder a él, hay que escribir Color::Blue o Feeling::Blue. Esto no solo resuelve el problema de colisiones, sino que también hace que el código sea autodocumentado: se ve inmediatamente a qué tipo pertenece el valor.
2️⃣ Conversión implícita a int (y viceversa)
El antiguo enum se convierte implícitamente a tipos enteros. A primera vista es conveniente, pero en la práctica es una fuente de errores difíciles de detectar.
// Antiguo enum peligrosoenum WarningLevel { Low, Medium, High };
void setVolume(int level) { if (level > 10) level = 10; // ...}
setVolume(High); // ¡Funciona! Pero High = 2, no es un nivel de volumen// Accidentalmente pasaste un nivel de alerta en lugar del volumen — el compilador callaEl compilador aceptará esto sin una sola advertencia. Y si confundes el orden de los argumentos en una función, el error pasará a tiempo de ejecución.
Cómo protege enum class:
enum class WarningLevel { Low, Medium, High };
void setVolume(int level);
setVolume(WarningLevel::High); // ¡Error de compilación!// No hay conversión implícita de enum class a intEl compilador te obligará a convertir explícitamente el valor si realmente es necesario. Esto significa que eres consciente de cada conversión, en lugar de confiar en un «quizás funcione».
// Si es realmente necesario — lo hacemos explícitamentesetVolume(static_cast<int>(WarningLevel::High)); // Explícito y claro3️⃣ Tipo base no definido
El antiguo enum no garantiza qué tipo entero se usará internamente. El compilador lo elige por sí mismo (normalmente int), y esto puede provocar sorpresas al transferir entre diferentes compiladores o plataformas.
// ¡El tamaño puede ser diferente!enum Small { A, B, C }; // Puede ser int, puede ser charenum Big { X = 100000, Y, Z }; // Sigue pudiendo ser intCon enum class puedes especificar explícitamente el tipo base, lo que te da control total