在 C++ 中迭代枚举 class 的常用方法是什么?

What are commonly-used ways to iterate over an enum class in C++?

我发现我所有用于迭代常规 enum 的标准技术不幸地不适用于 enum classes,因为枚举 类 不会隐式转换为整数。

不是 How can I iterate over an enum? 的副本,因为我问的是 enum class(即:强类型 枚举)而他们在问关于常规 enum(即:弱类型 枚举)。

这是我能想到的最易读和最简单的方法,但我对其他人的示例解决方案持开放态度。

我发现这种方法易于使用,类似于我的 C 方法(使其更便携和更易于识别),并且适用于 C++。它使用 -Wall -Wextra -Werror 编译器构建选项进行编译。

enum class MyErrorType 
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    _COUNT,
    // helpers for iterating over the enum
    begin = 0,
    end = _COUNT,
};

for (MyErrorType myErrorType = (MyErrorType)0; 
        myErrorType < MyErrorType::_COUNT;
        myErrorType = static_cast<MyErrorType>((size_t)myErrorType + 1)) 
{
    switch (myErrorType) 
    {
        case MyErrorType::SOMETHING_1:
            break;
        case MyErrorType::SOMETHING_2:
            break;
        case MyErrorType::SOMETHING_3:
            break;
        case MyErrorType::SOMETHING_4:
            break;
        case MyErrorType::SOMETHING_5:
            break;
        case MyErrorType::_COUNT:
            // This case will never be reached. It is included only so that when
            // compiling with `-Wall -Wextra -Werror` build flags you get the
            // added bonus of covering all switch cases (withOUT unnecessarily
            // relying on a `default` case which would break this feature!), so
            // if you ever add a new element to the enum class but forget to
            // add it here to the switch case the compiler will THROW AN ERROR.
            // This is an added safety benefit to force you to keep your enum
            // and the switch statement in-sync! It's a technique commonly used
            // in C as well.
            break;
    }
}

请阅读我对上述 MyErrorType::_COUNT 案例的评论!如果您使用编译器的 -Wall -Wextra -Werror 编译器选项但不在 switch 语句中包含这种情况(因为这些构建选项要求您在所有 switch 语句中涵盖所有枚举情况! ), 编译器会抛出如下错误并且stop!这是一个很好的安全功能,可确保您保持枚举定义和所有 switch case 同步,处理所有 switch 语句中的所有可能枚举。这是 LLVM 抛出的编译器错误 clang compiler (an alternative to gcc):

../my_file.cpp:11:16: error: enumeration value ‘_COUNT’ not handled in switch [-Werror=switch]
   11 |         switch (myErrorType) {
      |                ^

为清楚起见,对上述代码的另一个微小改进是将 beginend 元素添加到您的枚举中,如下所示:

enum class MyErrorType 
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    _COUNT,
    // helpers for iterating over the enum
    begin = 0,
    end = _COUNT,
};

...这样您就可以按如下方式遍历枚举。 for 循环的递增部分对于所有必需的转换仍然有点麻烦,但初始状态和结束条件检查现在至少更加清晰,因为它们使用 MyErrorType::beginMyErrorType::end:

for (MyErrorType myErrorType = MyErrorType::begin; 
        myErrorType < MyErrorType::end;
        myErrorType = static_cast<MyErrorType>((size_t)myErrorType + 1)) 
{
    switch (myErrorType) 
    {
        case MyErrorType::SOMETHING_1:
            break;
        case MyErrorType::SOMETHING_2:
            break;
        case MyErrorType::SOMETHING_3:
            break;
        case MyErrorType::SOMETHING_4:
            break;
        case MyErrorType::SOMETHING_5:
            break;
        case MyErrorType::_COUNT:
            // This case will never be reached.
            break;
    }
}

相关:

  1. 迭代 enums(与 enum classes 相对)的常用技术:How can I iterate over an enum?
    1. [我的回答]How can I iterate over an enum?
  2. 我对 enum classes(强类型 枚举)和常规 enums(弱类型)之间的一些差异的回答-typed 枚举)在 C++ 中:How to automatically convert strongly typed enum into int?
  3. Some of my personal notes on the -Wall -Wextra -Werror and other build options, from my eRCaGuy_hello_world 回购。
  4. Incrementation and decrementation of “enum class”

其他关键字:在C或C++中遍历枚举或枚举class的常用方法;在 C++ 中迭代枚举 class 的最佳方法;枚举 class C++ 迭代; C++ 遍历枚举 class

另一种选择是使用 C++20 范围来组成 enum 范围:

constexpr inline auto enum_range = [](auto front, auto back) {
  return std::views::iota(std::to_underlying(front), std::to_underlying(back) + 1) 
       | std::views::transform([](auto e) { return decltype(front)(e); }); 
};

然后你可以像这样迭代enum

enum class color { red, yellow, green, blue };
for (const auto e : enum_range(color::red, color::blue))
  // ...

demo.