在 C++ 中迭代枚举 class 的常用方法是什么?
What are commonly-used ways to iterate over an enum class in C++?
我发现我所有用于迭代常规 enum
的标准技术不幸地不适用于 enum class
es,因为枚举 类 不会隐式转换为整数。
不是 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) {
| ^
为清楚起见,对上述代码的另一个微小改进是将 begin
和 end
元素添加到您的枚举中,如下所示:
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::begin
和 MyErrorType::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;
}
}
相关:
- 迭代
enum
s(与 enum class
es 相对)的常用技术:How can I iterate over an enum?
- [我的回答]How can I iterate over an enum?
- 我对
enum class
es(强类型 枚举)和常规 enum
s(弱类型)之间的一些差异的回答-typed 枚举)在 C++ 中:How to automatically convert strongly typed enum into int?
- Some of my personal notes on the
-Wall -Wextra -Werror
and other build options, from my eRCaGuy_hello_world 回购。
- 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))
// ...
我发现我所有用于迭代常规 enum
的标准技术不幸地不适用于 enum class
es,因为枚举 类 不会隐式转换为整数。
不是 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) { | ^
为清楚起见,对上述代码的另一个微小改进是将 begin
和 end
元素添加到您的枚举中,如下所示:
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::begin
和 MyErrorType::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;
}
}
相关:
- 迭代
enum
s(与enum class
es 相对)的常用技术:How can I iterate over an enum?- [我的回答]How can I iterate over an enum?
- 我对
enum class
es(强类型 枚举)和常规enum
s(弱类型)之间的一些差异的回答-typed 枚举)在 C++ 中:How to automatically convert strongly typed enum into int? - Some of my personal notes on the
-Wall -Wextra -Werror
and other build options, from my eRCaGuy_hello_world 回购。 - 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))
// ...