C++ - enum vs enum class
In C++, enumerations can be defined as either unscoped enums (enum) or scoped enums (enum class or enum struct, introduced in C++11).
In modern C++, enum class is strongly preferred for type safety and clean scoping.
Key Differences
1. Scope and Name Pollution
enum(Unscoped): Enumerator names leak into the enclosing scope, causing potential naming collisions.enum class(Scoped): Enumerator names are scoped to the enum name and must be accessed withEnumName::Value.
// Unscoped enum: 'Red' leaks into global/enclosing scope
enum Color { Red, Green, Blue };
// enum Alert { Red, Yellow }; // Compilation Error: redefinition of 'Red'
// Scoped enum: No collisions
enum class TrafficLight { Red, Yellow, Green };
enum class ColorClass { Red, Green, Blue };
TrafficLight signal = TrafficLight::Red;
ColorClass color = ColorClass::Red;
2. Type Safety & Implicit Conversion
enum: Implicitly converts to integral types (int,bool, etc.), allowing accidental comparisons between unrelated enums.enum class: Strongly typed. No implicit conversions toint; explicit casting (static_cast<int>(...)) is required.
enum Status { Ok, Error };
enum Priority { Low, High };
// Unscoped enum enables unintended comparisons:
if (Ok == Low) {
// Compiles and evaluates to true because both are 0!
}
enum class StatusClass { Ok, Error };
enum class PriorityClass { Low, High };
// Scoped enum prevents accidental comparisons:
// if (StatusClass::Ok == PriorityClass::Low) {} // Compilation Error!
// int code = StatusClass::Ok; // Compilation Error!
int code = static_cast<int>(StatusClass::Ok); // Explicit cast works
3. Underlying Type Specification & Forward Declaration
Both unscoped and scoped enums allow specifying underlying types (e.g. uint8_t), but scoped enums default to int and can always be forward-declared without ambiguity.
// Forward declarations:
enum class TaskState : uint8_t; // OK
enum LegacyState : uint8_t; // OK only if underlying type is explicitly specified
// Defining underlying type:
enum class ByteFlag : uint8_t {
Read = 1 << 0,
Write = 1 << 1,
Exec = 1 << 2
};
4. C++20 using enum
In C++20, you can introduce scoped enum identifiers into local scope when writing repetitive switch statements without sacrificing type safety:
void handleLight(TrafficLight light) {
switch (light) {
using enum TrafficLight;
case Red: /* ... */ break;
case Yellow: /* ... */ break;
case Green: /* ... */ break;
}
}
Comparison Summary
| Feature | enum (Unscoped) |
enum class (Scoped) |
|---|---|---|
| Standard | C++98 / C | C++11 onwards |
| Scope | Leaks into enclosing scope | Scoped to enum identifier (Type::Value) |
Implicit int conversion |
Yes (weakly typed) | No (strongly typed, requires static_cast) |
| Default Underlying Type | Implementation-defined | int |
| Forward Declaration | Only if underlying type is given | Yes, always allowed |
| Recommended Usage | Interop with C APIs | Default for modern C++ |