C++ Enumerations (enum)
Lesson outline
Introduction to Enumerations
Enumerations (enums) in C++ are user-defined types that consist of a set of named integer constants (enumerators).
They improve code readability and maintainability by replacing 'magic numbers' with meaningful names.
Basic Enum Syntax
Enum Values and Underlying Type
By default (when you do not assign values explicitly):
- The first enumerator has value 0.
- Each subsequent enumerator increases by 1 from the previous enumerator's value.
- The underlying integer type of an unscoped enum is implementation-defined but must be able to represent all enumerator values.
Enumerators may be assigned explicit values (including negative values), and multiple enumerators may share the same value.
Custom Enum Values
Enumerators can be explicitly assigned integer values:
Scoped Enums (enum class) - C++11
C++11 introduced strongly-typed, scoped enums for safer use:
| Feature | Traditional enum | enum class (scoped) |
|---|---|---|
| Scope | Unscoped (enumerators appear in enclosing scope) | Scoped (use EnumName::Enumerator) |
| Type safety | Implicit conversion to int allowed | No implicit conversion to int |
| Underlying type | Implementation-defined (unless specified) | Specifiable (default: int) |
| Forward declaration | Allowed only if a fixed underlying type is specified (e.g., enum E : int;) | Supported (e.g., enum class E;) |
Specifying Underlying Type
You can specify the underlying integer type of an enum:
Forward Declarations
Forward declaration rules:
- Unscoped: you may forward-declare only if you fix the underlying type (e.g.,
enum E : int;). - Scoped: you may forward-declare without specifying the underlying type (defaults to
int).
Enum Operations
You can compare values of the same enum type. Arithmetic on scoped enums requires explicit conversion; unscoped enums permit more implicit conversions.
Bitmask Enums
Enums are commonly used for flags and bitmasks. With scoped enums, define bitwise operators to avoid repetitive casts.
Iterating Over Enums
Enums cannot be directly iterated. One approach is to add a sentinel enumerator, but this only works if values are contiguous starting at 0.
Real-World Example: State Machine
Enums are ideal for modeling state machines, such as traffic lights:
Best Practices
- Prefer
enum classfor type safety and scoping. - Use descriptive, consistent names for enumerators.
- For flags, assign powers of two and provide bitwise operator overloads.
- Specify the underlying type when ABI size matters or for bitmasks.
- Be cautious with iteration; maintain a list/array of values or a sentinel only when appropriate.
- Avoid casting between unrelated enum types.
- From C++23, prefer
std::to_underlyingfor conversions.