C++ 中迭代枚举类的常用方法有哪些?

问题描述 投票:0回答:2

不幸的是,我发现迭代常规

enum
的所有标准技术都不适用于
enum class
,因为枚举类不会隐式转换为整数。

不是如何迭代枚举?的重复,因为我问的是一个

enum class
(即:一个强类型枚举),而他们问的是一个常规的
enum
(即: 弱类型枚举)。

c++ enums enumeration enum-class
2个回答
11
投票

另一种选择是使用 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))
  // ...

演示。


0
投票

虽然存在其他复杂但极不可读的 C++20 技术,但以下方法具有以下优点:

  1. 对于任何人来说这是最简单的。
  2. 它的进入门槛最低。
  3. 它适用于任何版本的 C++,而不需要 C++20。还有...
  4. 它仍然有可能不向枚举中引入新的枚举值,而是为了方便起见,这种方法对枚举中已经存在的几个值进行命名别名。

它使用

-Wall -Wextra -Werror
编译器构建选项进行编译,这确保如果您忘记处理 switch 语句中的任何枚举值,则会引发编译时错误。这是一个很好的安全功能,可确保您保持枚举定义和所有 switch 情况同步,处理所有 switch 语句中所有可能的枚举。

来自我的 eRCaGuy_hello_world 存储库中的 enum_class_iterate.cpp

///usr/bin/env ccache g++ -Wall -Wextra -Werror -O3 -std=gnu++17 "$0" -o /tmp/a && /tmp/a "$@"; exit
// For the line just above, see my answer here: https://stackoverflow.com/a/75491834/4561887

#include <cstdio>   // For `printf()`

enum class MyErrorType 
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,
    // Helpers for iterating over the enum:
    // - Note: adding these helpers adds no new enum values, since `begin`
    //   already has the same value as `SOMETHING_1`, and `end` already has the
    //   same value as `SOMETHING_5`. These are just aliased names is all. 
    begin = 0,
    end = SOMETHING_5,
};

int main()
{
    printf("C++ enum class iteration demo.\n");

    // Iterate over the enum class

    // Option 1
    for (MyErrorType myErrorType = MyErrorType::begin;
        myErrorType <= MyErrorType::end;
        myErrorType = static_cast<MyErrorType>(
            static_cast<size_t>(myErrorType) + 1))
    {
        switch (myErrorType)
        {
            case MyErrorType::SOMETHING_1:
                printf("MyErrorType::SOMETHING_1\n");
                break;
            case MyErrorType::SOMETHING_2:
                printf("MyErrorType::SOMETHING_2\n");
                break;
            case MyErrorType::SOMETHING_3:
                printf("MyErrorType::SOMETHING_3\n");
                break;
            case MyErrorType::SOMETHING_4:
                printf("MyErrorType::SOMETHING_4\n");
                break;
            case MyErrorType::SOMETHING_5:
                printf("MyErrorType::SOMETHING_5\n");
                break;
        }
    }
}

使上面的文件可执行并按如下方式运行。在 Linux Ubuntu 22.04 中测试:

# Ensure you have `ccache`
sudo apt update
sudo apt install ccache

# make the file executable
chmod +x enum_class_iterate.cpp

# compile and run it
./enum_class_iterate.cpp

示例运行和输出:

eRCaGuy_hello_world/cpp$ ./enum_class_iterate.cpp 
C++ enum class iteration demo.
MyErrorType::SOMETHING_1
MyErrorType::SOMETHING_2
MyErrorType::SOMETHING_3
MyErrorType::SOMETHING_4
MyErrorType::SOMETHING_5

请注意,如果您需要在任何地方使用计数值来获取枚举类中有效枚举值的数量,您也可以选择添加计数值。这还有一个非常好的额外好处,即使枚举类定义的最后部分(从

count,
开始)对于所有枚举类始终完全相同,因此您可以轻松地复制粘贴此代码并在整个代码库中识别它。在上面的第一个示例中,在
end = SOMETHING_5,
中,必须为所有枚举手动更新
SOMETHING_5
枚举值,这很容易出错。 所以,这是我在 C++ 中定义枚举类的首选方法:

enum class MyErrorType2 
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,

    // Helpers
    count,
    begin = 0,
    end = count - 1,
};

对枚举的迭代仍然与上面完全相同,只是您必须在 switch 语句中为

count
值添加一个虚拟的“无事可做”情况,以便覆盖枚举类中的所有可能值:

// Option 2: same as above, except we must also include the `count` value
// as a switch case. 
for (MyErrorType2 myErrorType2 = MyErrorType2::begin;
    myErrorType2 <= MyErrorType2::end;
    myErrorType2 = static_cast<MyErrorType2>(
        static_cast<size_t>(myErrorType2) + 1))
{
    switch (myErrorType2)
    {
        case MyErrorType2::SOMETHING_1:
            printf("MyErrorType2::SOMETHING_1\n");
            break;
        case MyErrorType2::SOMETHING_2:
            printf("MyErrorType2::SOMETHING_2\n");
            break;
        case MyErrorType2::SOMETHING_3:
            printf("MyErrorType2::SOMETHING_3\n");
            break;
        case MyErrorType2::SOMETHING_4:
            printf("MyErrorType2::SOMETHING_4\n");
            break;
        case MyErrorType2::SOMETHING_5:
            printf("MyErrorType2::SOMETHING_5\n");
            break;
        case MyErrorType2::count:
            // Nothing to do
            break;
    }
}

如果您忘记了 switch 语句中的

case MyErrorType2::count:
大小写,但使用编译器的
-Wall -Wextra -Werror
选项进行编译,您将收到此错误:
error: enumeration value ‘count’ not handled in switch [-Werror=switch]
,如下所示:

eRCaGuy_hello_world/cpp$ ./enum_class_iterate.cpp 
./enum_class_iterate.cpp: In function ‘int main()’:
./enum_class_iterate.cpp:127:16: error: enumeration value ‘count’ not handled in switch [-Werror=switch]
  127 |         switch (myErrorType2)
      |                ^
cc1plus: all warnings being treated as errors

因此,建议在任何 switch 语句中使用

-Wall -Wextra -Werror
switch case 来使用 default
not
进行编译,以帮助您始终保持枚举定义和 switch 语句同步,涵盖所有可能的枚举值。

相关:

  1. 迭代
    enum
    (与
    enum class
    es 相对)的常用技术:如何迭代枚举?
    1. [我的答案] 如何迭代枚举?
  2. 我对C++中
    enum class
    es(强类型枚举)和常规
    enum
    s(弱类型枚举)之间的一些差异的回答:如何自动将强类型枚举转换为int?
  3. 我关于
    -Wall -Wextra -Werror
    和其他构建选项
    的一些个人笔记,来自我的 eRCaGuy_hello_world 存储库。
  4. “枚举类”的递增和递减

其他关键字:C或C++中迭代enum或enum类的常用方式;在 C++ 中迭代枚举类的最佳方法;枚举类 C++ 迭代; C++ 迭代枚举类

© www.soinside.com 2019 - 2024. All rights reserved.