第 18.4 節

enum class

0瀏覽次數0訪問次數--跳出率--平均停留

What problem does this section solve?

There are several issues with C language's enum:

  1. Name Pollution: Enum values leak into their surrounding scope. For example, after writing enum Color { red, green };, red becomes a global name and can no longer be used in another enum.
  2. Implicit conversion to integer: Enum values are implicitly converted to integers for operations (red + 1 is legal but usually meaningless).
  3. Underlying Type Not Specified: The compiler determines how much memory to allocate for storing enum values.

enum class solved all the above problems.

What is this feature?

enum class (scoped enumeration) is an improved enumeration introduced in C++11:

  • The enumeration values have scope limitations, and you need to access them using Color::red.
  • does not implicitly convert to an integer.
  • Can specify the underlying type (such as enum class Color : uint8_t).

C++ standard version

C++11

Required header files

No need for extra headers. enum class is a language keyword. To use fixed-width integers like uint8_t as the underlying type, you need to include #include <cstdint>.

Basic Syntax

enum class 枚举名 : 底层类型
{
    值1,
    值2,
    值3
};

// 访问
枚举名 变量 = 枚举名::值1;

Common Usage

UsageExplanation
enum class E { A, B, C };Basic Enum Class
An enumeration (enum) type used to define a set of named constants. Typically used to represent a fixed set of related values, such as days of the week, states, or categories, improving code readability and type safety.
enum class E : uint8_t { A, B, C };specify the underlying type
E::AAccessing enum values (with scope)
static_cast<int>(E::A)Explicit conversion (no implicit conversion)
switch (val) { case E::A: ... }switch matching

Example code

Example 1: Old enum vs enum class

#include <iostream>

// 旧式 enum(不推荐)
enum OldColor { red, green, blue };

// enum class(推荐)
enum class Color { red, green, blue };

int main()
{
    // 旧式 enum:名字污染,red 直接可见
    OldColor oc = red;  // 直接写 red
    int n = red;         // 隐式转为 0

    // enum class:需要带作用域
    Color c = Color::red;

    // int m = Color::red;  // ❌ 编译错误!不会隐式转换为 int
    int m = static_cast<int>(Color::red);  // ✅ 显式转换

    std::cout << "OldColor red = " << n << "\n";
    std::cout << "Color::red = " << m << "\n";

    return 0;
}

Results

OldColor red = 0
Color::red = 0

Example 2: Based on Example 1, specifying underlying types and custom values

#include <cstdint>
#include <iostream>

// 指定底层类型为 uint8_t(只占一个字节)
enum class Status : uint8_t
{
    Idle = 0,
    Running = 10,
    Paused = 20,
    Stopped = 30
};

void print_status(Status s)
{
    switch (s)
    {
        case Status::Idle:
            std::cout << "Idle\n";
            break;
        case Status::Running:
            std::cout << "Running\n";
            break;
        case Status::Paused:
            std::cout << "Paused\n";
            break;
        case Status::Stopped:
            std::cout << "Stopped\n";
            break;
    }
}

int main()
{
    Status s1 = Status::Running;
    print_status(s1);
    print_status(Status::Stopped);

    // 查看底层整数值
    std::cout << "sizeof(Status) = " << sizeof(Status) << " byte(s)\n";
    std::cout << "Running = " << static_cast<int>(Status::Running) << "\n";

    return 0;
}

Results

Running
Stopped
sizeof(Status) = 1 byte(s)
Running = 10

Example 3: Building on Example 2, the application of enum class within a struct.

#include <iostream>
#include <string>

enum class Grade
{
    A = 90,
    B = 80,
    C = 70,
    D = 60,
    F = 0
};

struct Student
{
    std::string name;
    Grade grade;
};

std::string grade_to_string(Grade g)
{
    switch (g)
    {
        case Grade::A: return "A (优秀)";
        case Grade::B: return "B (良好)";
        case Grade::C: return "C (中等)";
        case Grade::D: return "D (及格)";
        case Grade::F: return "F (不及格)";
        default:       return "未知";
    }
}

int main()
{
    Student s1{"Alice", Grade::A};
    Student s2{"Bob", Grade::C};

    std::cout << s1.name << " grade: " << grade_to_string(s1.grade) << "\n";
    std::cout << s2.name << " grade: " << grade_to_string(s2.grade) << "\n";

    // 比较(enum class 支持 ==, <, > 等)
    if (s1.grade > s2.grade)
    {
        std::cout << s1.name << " has higher grade\n";
    }

    return 0;
}

Results

Alice grade: A (优秀)
Bob grade: C (中等)
Alice has higher grade

runtime results

See the "running results" for each example above.

Key syntax explanation in the example

|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.


ExampleDiscusses whatNewly emerged syntaxWhy write it this wayPrecautions
Example 1Old enum vs enum classenum classstatic_cast<int>()Old enums have namespace pollution and implicit conversion to int; enum class resolves these issues.Explicit conversion is required when integer values are needed.
Example 2Specify underlying types and switch usageenum class : uint8_t、switch + enum classSpecifying the underlying type can save memory (e.g., in microcontroller scenarios).sizeof(enum class) does not equal the number of members
Example 3Using enum class within a struct in C++ provides a way to encapsulate enumerated types with strong typing and scope control. Here's a concise guide:

1. Basic Definition

struct Robot {
    enum class Direction { Forward, Backward, Left, Right };
    Direction currentDirection = Direction::Forward;
};

2. Accessing Members

Robot myRobot;
myRobot.currentDirection = Robot::Direction::Left;

3. Benefits

  • Type Safety: Prevents implicit conversions to/from integers.
  • Scoped Access: Requires explicit enum class namespacing (e.g., Robot::Direction::Forward).
  • Encapsulation: Keeps enums tied to the struct’s context, improving code organization.

4. Usage Example

void moveRobot(Robot& robot, Robot::Direction dir) {
    robot.currentDirection = dir;
}

// Call
moveRobot(myRobot, Robot::Direction::Right);

5. Best Practices

  • Prefer enum class over plain enum for safety.
  • Use it for attributes closely related to the struct’s purpose (e.g., states, modes).
  • Combine with struct member functions for cohesive design.

This pattern is common in robotics (e.g., defining sensor states or motor commands) and aligns with clean, maintainable code principles.|Using enum class as struct member, comparison operations|A finite set of values representing states, levels, etc.|Enum class supports comparisons using ==, <, >, etc.|

Enum class is well-suited for representing states and options.

enum class is best for representing "a limited number of discrete values," such as robot states, task phases, communication protocol commands, and UI modes.

Example 4: Representing robot states with enum class

#include <iostream>

enum class RobotState
{
    Idle,
    Running,
    Error
};

void handle_state(RobotState state)
{
    switch (state)
    {
        case RobotState::Idle:
            std::cout << "wait for command\n";
            break;
        case RobotState::Running:
            std::cout << "execute task\n";
            break;
        case RobotState::Error:
            std::cout << "stop and report error\n";
            break;
    }
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    RobotState state = RobotState::Running;
    handle_state(state);

    // 返回 0 表示程序正常结束。
    return 0;
}

Results

execute task

If using int state = 1, readers won’t know what 1 is; if using enum class RobotState::Running, the semantics are directly written in the type and won’t be confused with other enums’ Running.

Common Errors

Error 1: Directly using enum class values as array subscripts

enum class Color { red, green, blue };
int counts[3];
counts[Color::red];  // ❌ 编译错误!不会隐式转换

The correct approach: counts[static_cast<int>(Color::red)], or consider using a map in this scenario.

Error 2: forgetting to write the scope prefix

enum class Status { Running, Stopped };
Status s = Running;  // ❌ 编译错误!缺少 Status::

Correct approach: Status s = Status::Running;

Error 3: Mixing different enum class values in switch statement

switch (status)
{
    case Status::Running:  // ✅ 正确
    case Machine::Running: // ❌ 编译错误!不同枚举类型
        break;
}

Correct practice: in a switch statement, the case labels must be of the same enumeration type as the switch expression.

使用建议

  • 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
  • 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
  • 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
  • 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
  • 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

  1. Always use enum class instead of the old enum: type-safe, clear scoping.
  2. Can specify underlying type: Used in embedded scenarios with enum class : uint8_t to save space.
  3. Use static_cast explicitly when an integer value is required: Do not rely on implicit conversion.
  4. Enum classes are still integers, and can be switched on: combine with default branch handling for unknown values.

Summary

  • enum class avoids name pollution by accessing enum values through 枚举名::值.
  • Does not implicitly convert to an integer, requiring static_cast explicit conversion.
  • can specify the underlying type (enum class E : uint8_t).
  • Suitable for representing a finite discrete set of values such as states, levels, or options.
音乐页