Scoped enums in C++: why is enum class safer than old enum?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why is enum class your new best friend in C++?



Everyone who has started learning C++ or returned to it after a break sooner or later faces a dilemma: use the good old enum or its more modern counterpart enum class? On Stack Overflow, the question “Why is enum class considered safer to use than plain enum?” has gathered thousands of views — and this is no coincidence. Switching to scoped enum (that is the official name for enum class) is not just a fashion statement, but a real step towards more reliable and readable code.



Let's figure out what exactly the danger of the old enum is and how enum class helps avoid a whole class of errors. If you write in C++ and still use unscoped enum — this article is for you.



Top 3 errors of the old enum (and how enum class fixes them)



1️⃣ Global namespace pollution



The most common problem with the old enum is that its constants "leak" into the surrounding namespace. Imagine you have two enumerations:



// Bad: old enumenum Color { Red, Green, Blue };enum Feeling { Happy, Sad, Blue }; // Compilation error! Blue is already defined


The constant Blue from Color is already taken, and you cannot use the same name in Feeling. In a large project with hundreds of files, this leads to a real headache. You have to come up with long names like Color_Blue or COLOR_BLUE, which makes the code ugly.



Solution with enum class:



// Good: enum class (scoped enum)enum class Color { Red, Green, Blue };enum class Feeling { Happy, Sad, Blue }; // OK! No conflict


Now Blue lives inside its own class. To access it, you need to write Color::Blue or Feeling::Blue. This not only solves the collision problem but also makes the code self-documenting — it's immediately clear which type the value belongs to.



2️⃣ Implicit conversion to int (and back)



The old enum is implicitly converted to integer types. At first glance, this is convenient, but in practice, it is a source of hard-to-find bugs.



// Dangerous old enumenum WarningLevel { Low, Medium, High };

void setVolume(int level) { if (level > 10) level = 10; // ...}

setVolume(High); // Works! But High = 2, not a volume level// You accidentally passed an alarm level instead of volume — the compiler is silent


The compiler will swallow this without a single warning. And if you mix up the order of arguments in a function — the bug goes to runtime.



How enum class protects:



enum class WarningLevel { Low, Medium, High };

void setVolume(int level);

setVolume(WarningLevel::High); // Compilation error!// No implicit conversion from enum class to int


The compiler will force you to explicitly convert the value if it is really needed. This means you are aware of every conversion, rather than relying on "hope it works out".



// If you really need it — do it explicitlysetVolume(static_cast<int>(WarningLevel::High)); // Explicit and clear


3️⃣ Undefined underlying type



The old enum does not guarantee which exact integer type will be used under the hood. The compiler chooses it itself (usually int), and this can lead to surprises when passing between different compilers or platforms.



// The size can be different!enum Small { A, B, C }; // Could be int, could be charenum Big { X = 100000, Y, Z }; // Could still be int


With enum class, you can explicitly specify the underlying type, giving you full control

Blogs

Book Recommendations