我只是注意到不能在枚举上使用标准数学运算符,如++或+=。

那么,在c++枚举中遍历所有值的最佳方法是什么?


当前回答

将变量强制转换为int&类型可以在保持类型可读的同时进行递增。

#include <iostream>

enum MyEnum
{
    ONE,
    TWO,
    THREE,
    FOUR,
};

int main()
{
    for (MyEnum v = MyEnum::ONE; v <= MyEnum::FOUR; ++(int&)v)
    {
        std::cout<<v<<std::endl;
    }

    return 0;
}
0
1
2
3

其他回答

优点:枚举可以有你喜欢的任意顺序的任何值,并且仍然很容易迭代它们。 名称和值只定义一次,在第一个#define中。

缺点:如果你在工作中使用这个短语,你需要用一整段话来向你的同事解释。而且,必须声明内存来给你的循环提供迭代的东西是很烦人的,但我不知道有什么解决办法不限制你使用相邻值的枚举(如果枚举总是有相邻值,enum可能不会给你买那么多)。

//create a, b, c, d as 0, 5, 6, 7
#define LIST x(a) x(b,=5) x(c) x(d)
#define x(n, ...) n __VA_ARGS__,
enum MyEnum {LIST}; //define the enum
#undef x //needed
#define x(n,...) n ,
MyEnum myWalkableEnum[] {LIST}; //define an iterable list of enum values
#undef x //neatness

int main()
{
  std::cout << d;
  for (auto z : myWalkableEnum)
    std::cout << z;
}
//outputs 70567

使用未定义的宏包装器声明一个列表,然后在各种情况下以不同的方式定义包装器的技巧,除了这个应用程序之外,还有许多应用程序。

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元素两次,并保持两个列表同步。

我经常这样做

    enum EMyEnum
    {
        E_First,
        E_Orange = E_First,
        E_Green,
        E_White,
        E_Blue,
        E_Last
    }

    for (EMyEnum i = E_First; i < E_Last; i = EMyEnum(i + 1))
    {}

或者如果不是连续的,但有规则步长(例如位标志)

    enum EAnimalCaps
    {
        E_None    = 0,
        E_First   = 0x1,
        E_CanFly  = E_First,
        E_CanWalk = 0x2
        E_CanSwim = 0x4,
        E_Last
    }
    
    class MyAnimal
    {
       EAnimalCaps m_Caps;
    }

    class Frog
    {
        Frog() : 
            m_Caps(EAnimalCaps(E_CanWalk | E_CanSwim))
        {}
    }

    for (EAnimalCaps= E_First; i < E_Last; i = EAnimalCaps(i << 1))
    {}

只需创建一个int型数组并遍历该数组,但使最后一个元素为-1并使用它作为退出条件。

enum为:

enum MyEnumType{Hay=12,Grass=42,Beer=39};

然后创建数组:

int Array[] = {Hay,Grass,Beer,-1};

for (int h = 0; Array[h] != -1; h++){
  doStuff( (MyEnumType) Array[h] );
}

当然,只要-1检查不与任何一个元素冲突,无论表示中的整数都不会被分解。

如果你不喜欢用最终的COUNT项污染你的枚举(因为如果你也在开关中使用枚举,那么编译器会警告你缺少大小写COUNT:),你可以这样做:

enum Colour {Red, Green, Blue};
const Colour LastColour = Blue;

Colour co(0);
while (true) {
  // do stuff with co
  // ...
  if (co == LastColour) break;
  co = Colour(co+1);
}