我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
当前回答
假设枚举是按顺序编号是容易出错的。此外,您可能只希望迭代选定的枚举数。如果这个子集很小,显式遍历它可能是一个优雅的选择:
enum Item { Man, Wolf, Goat, Cabbage }; // or enum class
for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
// ...
}
其他回答
typedef enum{
first = 2,
second = 6,
third = 17
}MyEnum;
static const int enumItems[] = {
first,
second,
third
}
static const int EnumLength = sizeof(enumItems) / sizeof(int);
for(int i = 0; i < EnumLength; i++){
//Do something with enumItems[i]
}
(把Marski的回答当作一个很大的暗示……)
由于枚举定义的主体与初始化列表相同,如果我们使用一个简单的宏来写出值,就可以在不重复项目列表的情况下做到这一点:
#define ITEM_LIST_MACRO Wolf, Goat, Cabbage
enum Item { ITEM_LIST_MACRO }; // Define the enum
// Now iterate through it
for (auto item : { ITEM_LIST_MACRO }) {
}
优点:简单,没有重复,不需要保持恼人的第一个/最后一个哨兵值。(事实上,我认为这可能是目前为止建议的唯一解决方案,不需要用户记得在新项目添加到列表时更新“结束”标记。)
缺点:不适用于范围限定的枚举(enum类),因为初始化列表需要范围(Item::Wolf等)。如果你想指定枚举成员的值而不是默认值,同样不起作用。
典型的方式如下:
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
请注意,枚举Last将被迭代跳过。利用这个“假的”Last enum,你不必每次想要添加一个新enum时都将for循环中的终止条件更新为最后一个“真实的”enum。 如果你以后想添加更多的枚举,只要在Last之前添加它们。本例中的循环仍然有效。
当然,如果指定了enum值,则会中断:
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
这说明枚举并不是真正用于遍历的。处理枚举的典型方法是在switch语句中使用它。
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
如果你真的想要枚举,把枚举值填充到一个向量中,然后遍历它。这也将正确地处理指定的enum值。
扩展@Eponymous的回答:它很棒,但没有提供通用语法。这是我想到的:
// Common/EnumTools.h
#pragma once
#include <array>
namespace Common {
// Here we forward-declare metafunction for mapping enums to their values.
// Since C++<23 doesn't have reflection, you have to populate it yourself :-(
// Usage: After declaring enum class E, add this overload in the namespace of E:
// inline constexpr auto allValuesArray(const E&, Commob::EnumAllValuesTag) { return std::array{E::foo, E::bar}; }
// Then `AllValues<NS::E>` will call `allValuesArray(NS::E{}, EnumAllValuesTag)` which will resolve
// by ADL.
// Just be sure to keep it sync'd with your enum!
// Here's what you want to use in, e.g., loops: "for (auto val : Common::AllValues<MyEnum>) {"
struct EnumAllValuesTag {}; // So your allValuesArray function is clearly associated with this header.
template <typename Enum>
static inline constexpr auto AllValues = allValuesArray(Enum{}, EnumAllValuesTag{});
// ^ Just "constexpr auto" or "constexpr std::array<Enum, allValuesArray(Enum{}, EnumAllValuesTag{}).size()>" didn't work on all compilers I'm using, but this did.
} // namespace Common
然后在你的命名空间:
#include "Common/EnumTools.h"
namespace MyNamespace {
enum class MyEnum {
foo,
bar = 4,
baz = 42,
};
// Making this not have to be in the `Common` namespace took some thinking,
// but is a critical feature since otherwise there's no hope in keeping it sync'd with the enum.
inline constexpr auto allValuesArray(const MyEnum&, Common::EnumAllValuesTag) {
return std::array{ MyEnum::foo, MyEnum::bar, MyEnum::baz };
}
} // namespace MyNamespace
然后在任何需要使用它的地方:
for (const auto& e : Common::AllValues<MyNamespace::MyEnum>) { ... }
所以即使你有typeded:
namespace YourNS {
using E = MyNamespace::MyEnum;
} // namespace YourNS
for (const auto& e : Common::AllValues<YourNS::E>) { ... }
我想不出比这更好的了,除了每个人都想要的实际语言功能。
未来工作:
您应该能够添加一个constexpr函数(以及一个元函数)来过滤Common::AllValues<E>,从而为枚举具有重复数值的情况提供一个Common::AllDistinctValues<E>,例如enum {foo = 0, bar = 0};。 我打赌有一种方法可以使用编译器的switch- coverage -all-enum-values来编写allValuesArray,这样如果枚举添加了一个值,它就会出错。
您可以尝试并定义以下宏:
#define for_range(_type, _param, _A1, _B1) for (bool _ok = true; _ok;)\
for (_type _start = _A1, _finish = _B1; _ok;)\
for (int _step = 2*(((int)_finish)>(int)_start)-1;_ok;)\
for (_type _param = _start; _ok ; \
(_param != _finish ? \
_param = static_cast<_type>(((int)_param)+_step) : _ok = false))
现在你可以使用它:
enum Count { zero, one, two, three };
for_range (Count, c, zero, three)
{
cout << "forward: " << c << endl;
}
它可以用来在无符号、整数、枚举和字符之间来回迭代:
for_range (unsigned, i, 10,0)
{
cout << "backwards i: " << i << endl;
}
for_range (char, c, 'z','a')
{
cout << c << endl;
}
尽管它的定义很尴尬,但它优化得很好。我看了一下vc++中的反汇编程序。 代码非常高效。不要推迟,但是三个for语句:编译器在优化后只会产生一个循环!你甚至可以定义封闭的循环:
unsigned p[4][5];
for_range (Count, i, zero,three)
for_range(unsigned int, j, 4, 0)
{
p[i][j] = static_cast<unsigned>(i)+j;
}
显然,不能在有间隙的枚举类型中进行迭代。