C++

Enums & Enum Classes

10 question(s)

What is an enumeration (enum) in C++?

Beginner
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++.
enum Color { Red, Green, Blue };
Color c = Green;
Real-world example Named enumerators replace magic numbers for states, making the code self-documenting.

Common follow-ups: What is the default underlying type? | Do enumerators convert to int?

Enums & Enum Classes Modern C++ Enums & Enum Classes

What is an enum class (scoped enum) and why is it preferred?

Beginner
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.

Common follow-ups: How does it improve type safety? | Why must you qualify the enumerators?

Enums & Enum Classes Casting & Conversions Enums & Enum Classes

How do you specify the underlying type of an enum?

Intermediate
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.

Common follow-ups: Why control the underlying type? | What is the default for enum class?

Enums & Enum Classes Memory Management Enums & Enum Classes

How do you convert between an enum class and its underlying integer?

Intermediate
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.

Common follow-ups: Why is the conversion explicit? | What does std::to_underlying do?

Casting & Conversions Enums & Enum Classes Enums & Enum Classes

How do you assign specific values to enumerators?

Beginner
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.

Common follow-ups: What value does an unassigned enumerator take? | Why assign explicit values?

Enums & Enum Classes Enums & Enum Classes Enums & Enum Classes

How do you implement type-safe bit flags with enum class?

Advanced
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.

Common follow-ups: Why do you need to overload operators? | How do you test a flag?

Operator Overloading Enums & Enum Classes Enums & Enum Classes

Can you forward declare an enum, and what are the rules?

Intermediate
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.

Common follow-ups: Why can't a plain enum be forward declared? | What makes an enum forward-declarable?

Compilation & Linking Enums & Enum Classes Enums & Enum Classes

What is the difference between an enum and a set of #define constants?

Beginner
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.

Common follow-ups: Why are enums safer than #define? | Do enums appear in the debugger?

Preprocessor & Macros Enums & Enum Classes Enums & Enum Classes

How can you convert an enum to a string (reflection limitations)?

Advanced
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.

Common follow-ups: Does C++ have built-in enum reflection? | What library helps?

Enums & Enum Classes Strings & string_view Enums & Enum Classes

What are the scoping and name-collision advantages of enum class?

Intermediate
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.

Common follow-ups: Why do unscoped enums clash? | How does scoping prevent collisions?

Namespaces Enums & Enum Classes Enums & Enum Classes