Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A scoped enumeration (introduced in C++11 via enum class or enum struct) is a distinct user-defined type consisting of a set of named integral constants, known as enumerators. Unlike traditional unscoped enumerations (enum), scoped enumerations are strongly typed and strongly scoped, preventing implicit type conversions and avoiding namespace pollution by confining the enumerators strictly to the enumeration’s scope.

Syntax

enum class EnumName : underlying_type {
    Enumerator1,
    Enumerator2 = constant_expression
};
  • enum class and enum struct are semantically identical.
  • underlying_type is optional. If omitted, the compiler defaults to int. It can be any integral type (e.g., unsigned char, std::int32_t, short).

Core Mechanics

1. Strong Scoping

Enumerators defined inside a scoped enumeration are not exported to the enclosing scope. They must be accessed using the scope resolution operator (::). To alleviate verbosity, C++20 introduced the using enum declaration, which explicitly brings scoped enumerators into the local scope.
enum class Color { Red, Green, Blue };

Color c1 = Color::Red;  // Correct: Explicitly scoped
// Color c2 = Red;      // Error: 'Red' was not declared in this scope
// int Red = 5;         // Valid: No name collision in the global scope

void processColor() {
    using enum Color;   // C++20: Brings Color enumerators into local scope
    Color c3 = Green;   // Valid: 'Green' is now visible
}

2. Strong Typing and Initialization

Scoped enumerations do not implicitly convert to their underlying integral type, nor do they implicitly convert to boolean or other enumeration types. Since C++17, a scoped enumeration can be initialized directly from an integer using direct-list-initialization, provided the value does not require a narrowing conversion.
enum class Status { Success = 0, Failure = 1 };

Status s1 = Status::Success;

// int val = s1;                         // Error: Cannot implicitly convert 'Status' to 'int'
// if (s1) { }                           // Error: Cannot implicitly convert to 'bool'

Status s2{1};                            // C++17 Valid: Direct-list-initialization
// Status s3 = 1;                        // Error: Copy-initialization is not allowed

3. Bitwise Operations Limitation

Because scoped enumerations strictly prohibit implicit conversions to their underlying integral types, standard bitwise operations (|, &, ^, ~) are not implicitly defined. Attempting to combine bitmask-style enumerators will result in a compiler error. To use a scoped enumeration as a bitmask, developers must explicitly overload the required bitwise operators.
enum class Flags : unsigned char {
    None  = 0x00,
    Read  = 0x01,
    Write = 0x02
};

// Flags f1 = Flags::Read | Flags::Write; // Error: no match for 'operator|'

// Explicitly overloading the bitwise OR operator
constexpr Flags operator|(Flags lhs, Flags rhs) {
    return static_cast<Flags>(
        static_cast<unsigned char>(lhs) | static_cast<unsigned char>(rhs)
    );
}

Flags f2 = Flags::Read | Flags::Write;    // Valid

4. Extracting the Underlying Value

Because implicit conversions are disabled, extracting the integer value requires explicit action. Historically, this required a static_cast. C++23 introduced std::to_underlying in the <utility> header as the standard, idiomatic way to extract the underlying value.
#include <utility>

enum class Permissions { Read = 4, Write = 2, Execute = 1 };
Permissions p = Permissions::Read;

// C++23: Idiomatic extraction
auto val = std::to_underlying(p);        // val is deduced as int, value is 4

// Pre-C++23 approach
int oldVal = static_cast<int>(p);        // Explicit cast yields 4

5. Explicit Underlying Types

You can explicitly define the memory footprint of the enumeration by specifying the underlying integral type. This guarantees the exact size of the type, which is critical for binary compatibility, serialization, or memory-constrained environments.
// Forces the enumeration to occupy exactly 1 byte
enum class State : unsigned char {
    Idle = 0x00,
    Active = 0x01
};

static_assert(sizeof(State) == 1, "State must be exactly 1 byte");

6. Forward Declarations

Because scoped enumerations have a deterministic underlying type (either explicitly declared or defaulting to int), the compiler knows their exact size. This allows scoped enumerations to be forward-declared, reducing compilation dependencies in header files.
#include <cstdint>

// Forward declaration
enum class ConnectionState : std::uint8_t;

// Usage in a function signature before definition
void handleConnection(ConnectionState state);

// Definition elsewhere
enum class ConnectionState : std::uint8_t {
    Disconnected,
    Connecting,
    Connected
};
Master C++ with Deep Grasping Methodology!Learn More