enum Color { Red, Green, Blue };
Color c = Green;
C++
Topics in this category
Casting & Conversions
Compilation & Linking
Concepts & Constraints
Concurrency
Const Correctness
Constexpr & Compile-Time
Coroutines
Enums & Enum Classes
Exception Handling
I/O Streams
Lambda Expressions
Memory Management
Modern C++
Move Semantics
Namespaces
OOP & Classes
Operator Overloading
Preprocessor & Macros
RAII & Smart Pointers
Ranges & Views
C++
Enums & Enum Classes
An enum defines a type with a set of named integer constants (enumerators), improving readability over magic numbers. Unscoped enums (enum Color { Red, Green };) put the names in the enclosing scope and implicitly convert to int; scoped enums (enum class) are safer and preferred in modern C++.
Real-world example
Named enumerators replace magic numbers for states, making the code self-documenting.
Enums & Enum Classes
Modern C++
Enums & Enum Classes
An enum class (scoped enumeration, C++11) scopes its enumerators to the enum's name (Color::Red) and does not implicitly convert to int, preventing accidental mixing with numbers or other enums. This gives stronger type safety and avoids name clashes, so enum class is preferred over plain enum in modern code.
enum class Color { Red, Green, Blue };
Color c = Color::Red;
// int x = c; // error: no implicit conversion
Real-world example
Using enum class for Status and Priority prevents accidentally comparing a Status to a Priority.
Enums & Enum Classes
Casting & Conversions
Enums & Enum Classes
You can specify the integer type backing an enum with a colon: enum class Flag : std::uint8_t { ... }. This controls the size and range, useful for memory layout, serialization, and interoperability. Scoped enums default to int; specifying the type also allows forward declaration.
enum class Flag : std::uint8_t { A = 1, B = 2, C = 4 };
Real-world example
A protocol enum uses uint8_t as its underlying type to match a one-byte wire field.
Enums & Enum Classes
Memory Management
Enums & Enum Classes
Because enum class doesn't convert implicitly, you convert explicitly with static_cast: static_cast<int>(e) to get the value, and static_cast<Enum>(i) to build an enum from an integer. C++23 adds std::to_underlying for a clearer enum-to-integer conversion. Explicit conversion keeps the type safety while allowing needed interop.
enum class E : int { X = 5 };
int i = static_cast<int>(E::X); // 5
E e = static_cast<E>(5);
// C++23: int j = std::to_underlying(E::X);
Real-world example
Serializing an enum class to a file requires an explicit static_cast to its underlying int.
Casting & Conversions
Enums & Enum Classes
Enums & Enum Classes
You can assign explicit integer values to enumerators; unassigned ones continue incrementing from the previous value. This is used to match external codes, create bit flags (powers of two), or leave gaps. Enumerators without values start at 0 and increase by 1.
enum class Http { OK = 200, NotFound = 404, Error = 500 };
Real-world example
HTTP status codes are modeled as an enum with their real numeric values for clarity.
Enums & Enum Classes
Enums & Enum Classes
Enums & Enum Classes
Using enum class with power-of-two values gives type safety but loses the built-in bitwise operators (no implicit int conversion). You restore them by overloading operator| , operator& , etc., for the enum, keeping flag combinations type-safe. Libraries or a small macro/helper template are often used to generate these operators.
enum class Perm : unsigned { R=1, W=2, X=4 };
constexpr Perm operator|(Perm a, Perm b){
return Perm(unsigned(a)|unsigned(b));
}
Real-world example
File permissions use an enum class with overloaded bitwise operators for safe flag combinations.
Operator Overloading
Enums & Enum Classes
Enums & Enum Classes
A scoped enum (or an unscoped enum with an explicit underlying type) can be forward declared because its size is known. A plain unscoped enum without a specified underlying type cannot be forward declared, since its size depends on its values. Forward declaration reduces header dependencies.
enum class Color : int; // forward declaration OK
enum Plain; // error without underlying type
Real-world example
Forward declaring an enum class in a header avoids pulling in the full definition, cutting compile dependencies.
Compilation & Linking
Enums & Enum Classes
Enums & Enum Classes
Enum enumerators are typed, scoped (for enum class), visible to the debugger, and grouped as a type, whereas #define constants are untyped preprocessor text substitutions with no scope or type safety and no debug symbol. Enums (especially enum class) are strongly preferred over #define for related constants.
enum class State { On, Off }; // typed, debuggable
// vs: #define ON 0 (untyped, no scope)
Real-world example
Replacing a group of #define state constants with an enum class adds type checking and debugger names.
Preprocessor & Macros
Enums & Enum Classes
Enums & Enum Classes
C++ has no built-in enum reflection before C++26 proposals, so enum-to-string is done manually (switch or a map), by code generation (X-macros), or with libraries like magic_enum (which use compile-time tricks). This is a common pain point; the standard approaches are a switch returning literals or a lookup table.
const char* name(Color c){
switch(c){ case Color::Red: return "Red"; default: return "?"; }
}
Real-world example
A logging system maps enum values to names via a switch so logs are human-readable.
Enums & Enum Classes
Strings & string_view
Enums & Enum Classes
With enum class, enumerators live in the enum's scope, so two enums can both have an enumerator named, say, 'None' without clashing (Mode::None vs Color::None). Unscoped enums leak their enumerators into the surrounding scope, causing collisions. Scoping is a key reason enum class is preferred.
enum class A { None };
enum class B { None }; // no clash
Real-world example
Two subsystems each define an enumerator 'Error' without conflict because they use enum class.
Namespaces
Enums & Enum Classes
Enums & Enum Classes