我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
我只是注意到不能在枚举上使用标准数学运算符,如++或+=。
那么,在c++枚举中遍历所有值的最佳方法是什么?
当前回答
典型的方式如下:
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值。
其他回答
如果枚举以0开头,且增量始终为1。
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
如果没有,我想唯一的原因是创造一个像
vector<enumType> vEnums;
添加条目,并使用普通的迭代器....
在c++11中,实际上有一个替代方案:编写一个模板化的自定义迭代器。
让我们假设枚举是
enum class foo {
one,
two,
three
};
这段泛型代码将会非常有效地达到目的——放置在泛型头文件中,它将为你提供任何你可能需要迭代的枚举:
#include <type_traits>
template < typename C, C beginVal, C endVal>
class Iterator {
typedef typename std::underlying_type<C>::type val_t;
int val;
public:
Iterator(const C & f) : val(static_cast<val_t>(f)) {}
Iterator() : val(static_cast<val_t>(beginVal)) {}
Iterator operator++() {
++val;
return *this;
}
C operator*() { return static_cast<C>(val); }
Iterator begin() { return *this; } //default ctor is good
Iterator end() {
static const Iterator endIter=++Iterator(endVal); // cache it
return endIter;
}
bool operator!=(const Iterator& i) { return val != i.val; }
};
你需要专门化它
typedef Iterator<foo, foo::one, foo::three> fooIterator;
然后你可以使用range-for进行迭代
for (foo i : fooIterator() ) { //notice the parentheses!
do_stuff(i);
}
枚举中没有空白的假设仍然成立;没有假设实际需要多少位来存储枚举值(感谢std::underlying_type)
在评论中已经有关于std::initializer_list (c++ 11)的讨论。 我提到example是为了遍历枚举。
或者std::initializer_list和一个更简单的语法:
enum E {
E1 = 4,
E2 = 8,
// ..
En
};
constexpr std::initializer_list<E> all_E = {E1, E2, /*..*/ En};
然后
for (auto e : all_E) {
// Do job with e
}
参考链接
enum class A {
a0=0, a3=3, a4=4
};
constexpr std::array<A, 3> ALL_A {A::a0, A::a3, A::a4}; // constexpr is important here
for(A a: ALL_A) {
if(a==A::a0 || a==A::a4) std::cout << static_cast<int>(a);
}
constexpr std::array甚至可以迭代非顺序的枚举,而无需编译器实例化数组。这取决于编译器的优化启发式以及是否取数组的地址。
In my experiments, I found that g++ 9.1 with -O3 will optimize away the above array if there are 2 non-sequential values or quite a few sequential values (I tested up to 6). But it only does this if you have an if statement. (I tried a statement that compared an integer value greater than all the elements in a sequential array and it inlined the iteration despite none being excluded, but when I left out the if statement, the values were put in memory.) It also inlined 5 values from a non-sequential enum in [one case|https://godbolt.org/z/XuGtoc]. I suspect this odd behavior is due to deep heuristics having to do with caches and branch prediction.
这里有一个godbolt的简单测试迭代的链接,演示了数组并不总是被实例化。
这种技术的代价是写入enum元素两次,并保持两个列表同步。
对于MS编译器:
#define inc_enum(i) ((decltype(i)) ((int)i + 1))
enum enumtype { one, two, three, count};
for(enumtype i = one; i < count; i = inc_enum(i))
{
dostuff(i);
}
注意:这比简单的模板化自定义迭代器答案要少得多。
你可以通过使用typeof而不是decltype来让它与GCC一起工作,但我现在没有那个编译器来确保它能编译。