简体   繁体   English

如何迭代枚举?

[英]How can I iterate over an enum?

I just noticed that you can not use standard math operators on an enum such as ++ or += .我刚刚注意到您不能在+++=enum上使用标准数学运算符。

So what is the best way to iterate through all of the values in a C++ enum ?那么遍历 C++ enum中所有值的最佳方法是什么?

The typical way is as follows:典型的方式如下:

enum Foo {
  One,
  Two,
  Three,
  Last
};

for ( int fooInt = One; fooInt != Last; fooInt++ )
{
   Foo foo = static_cast<Foo>(fooInt);
   // ...
}

Please note, the enum Last is meant to be skipped by the iteration.请注意,枚举Last旨在被迭代跳过。 Utilizing this "fake" Last enum, you don't have to update your terminating condition in the for loop to the last "real" enum each time you want to add a new enum.利用这个“假” Last枚举,您不必在每次要添加新枚举时将 for 循环中的终止条件更新为最后一个“真实”枚举。 If you want to add more enums later, just add them before Last.如果您想稍后添加更多枚举,只需在 Last 之前添加它们。 The loop in this example will still work.此示例中的循环仍然有效。

Of course, this breaks down if the enum values are specified:当然,如果指定了枚举值,这将失效:

enum Foo {
  One = 1,
  Two = 9,
  Three = 4,
  Last
};

This illustrates that an enum is not really meant to iterate through.这说明枚举并不是真的要迭代。 The typical way to deal with an enum is to use it in a switch statement.处理枚举的典型方法是在 switch 语句中使用它。

switch ( foo )
{
    case One:
        // ..
        break;
    case Two:  // intentional fall-through
    case Three:
        // ..
        break;
    case Four:
        // ..
        break;
     default:
        assert( ! "Invalid Foo enum value" );
        break;
}

If you really want to enumerate, stuff the enum values in a vector and iterate over that.如果您真的想枚举,请将枚举值填充到向量中并对其进行迭代。 This will properly deal with the specified enum values as well.这也将正确处理指定的枚举值。

#include <iostream>
#include <algorithm>

namespace MyEnum
{
  enum Type
  {
    a = 100,
    b = 220,
    c = -1
  };

  static const Type All[] = { a, b, c };
}

void fun( const MyEnum::Type e )
{
  std::cout << e << std::endl;
}

int main()
{
  // all
  for ( const auto e : MyEnum::All )
    fun( e );

  // some
  for ( const auto e : { MyEnum::a, MyEnum::b } )
    fun( e );

  // all
  std::for_each( std::begin( MyEnum::All ), std::end( MyEnum::All ), fun );

  return 0;
}

With c++11, there actually is an alternative: writing a templatized custom iterator.使用 c++11,实际上有一个替代方案:编写一个模板化的自定义迭代器。

let's assume your enum is假设您的枚举是

enum class foo {
  one,
  two,
  three
};

This generic code will do the trick, quite efficiently - place in a generic header, it'll serve you for any enum you may need to iterate over:这个通用代码将非常有效地解决问题 - 放置在通用标头中,它将为您可能需要迭代的任何枚举提供服务:

#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; }
};

You'll need to specialize it你需要专攻它

typedef Iterator<foo, foo::one, foo::three> fooIterator;

And then you can iterate using range-for然后您可以使用 range-for 进行迭代

for (foo i : fooIterator() ) { //notice the parentheses!
   do_stuff(i);
}

The assumption that you don't have gaps in your enum is still true;您的枚举中没有空白的假设仍然是正确的; there is no assumption on the number of bits actually needed to store the enum value (thanks to std::underlying_type)没有假设存储枚举值实际需要的位数(感谢 std::underlying_type)

too much complicated these solution, i do like that :这些解决方案太复杂了,我喜欢这样:

enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};

const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };

for (NodePosition pos : NodePositionVector) {
...
}

I often do it like that我经常这样做

    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))
    {}

or if not successive, but with regular step (eg bit flags)或者如果不是连续的,但有规律的步骤(例如位标志)

    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))
    {}

If your enum starts with 0 and the increment is always 1.如果您的枚举以 0 开头并且增量始终为 1。

enum enumType 
{ 
    A = 0,
    B,
    C,
    enumTypeEnd
};

for(int i=0; i<enumTypeEnd; i++)
{
   enumType eCurrent = (enumType) i;            
}

If not I guess the only why is to create something like a如果不是,我猜唯一的原因是创建类似

vector<enumType> vEnums;

add the items, and use normal iterators....添加项目,并使用普通迭代器....

Something that hasn't been covered in the other answers = if you're using strongly typed C++11 enums, you cannot use ++ or + int on them.其他答案中未涵盖的内容=如果您使用的是强类型 C++11 枚举,则不能在它们上使用+++ int In that case, a bit of a messier solution is required:在这种情况下,需要一些更混乱的解决方案:

enum class myenumtype {
  MYENUM_FIRST,
  MYENUM_OTHER,
  MYENUM_LAST
}

for(myenumtype myenum = myenumtype::MYENUM_FIRST;
    myenum != myenumtype::MYENUM_LAST;
    myenum = static_cast<myenumtype>(static_cast<int>(myenum) + 1)) {

  do_whatever(myenum)

}

You can't with an enum.你不能用枚举。 Maybe an enum isn't the best fit for your situation.也许枚举不是最适合您的情况。

A common convention is to name the last enum value something like MAX and use that to control a loop using an int.一个常见的约定是将最后一个枚举值命名为 MAX,并使用它来控制使用 int 的循环。

Assuming that enum is numbered sequentially is error prone.假设枚举按顺序编号很容易出错。 Moreover, you may want to iterate over selected enumerators only.此外,您可能只想迭代选定的枚举数。 If that subset is small, looping over it explicitly might be an elegant choice:如果该子集很小,则显式循环它可能是一个优雅的选择:

enum Item { Man, Wolf, Goat, Cabbage }; // or enum class

for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
    // ...
}
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);
}

A constexpr std::array can iterate even non-sequential enums without the array being instantiated by the compiler. constexpr std::array甚至可以迭代非顺序枚举,而无需编译器实例化数组。 This depends on things like the compiler's optimization heuristics and whether you take the array's address.这取决于编译器的优化启发式方法以及您是否采用数组的地址。

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).在我的实验中,我发现如果有 2 个非连续值或相当多的连续值(我测试了多达 6 个),带有-O3g++ 9.1 将优化上述数组。 But it only does this if you have an if statement.但只有当你有if语句时它才会这样做。 (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| (我尝试了一个语句,它比较了一个大于顺序数组中所有元素的整数值,尽管没有排除任何元素,但它内联了迭代,但是当我省略 if 语句时,这些值被放入内存中。)它还内联了 5 [one case| 中非顺序枚举的值] https://godbolt.org/z/XuGtoc] . https://godbolt.org/z/XuGtoc] I suspect this odd behavior is due to deep heuristics having to do with caches and branch prediction.我怀疑这种奇怪的行为是由于深度启发式与缓存和分支预测有关。

Here is a link to a simple test iteration on godbolt that demonstrates the array does not always get instantiated.这是godbolt 上一个简单测试迭代的链接,它演示了数组并不总是被实例化。

The price of this technique is writing the enum elements twice and keeping the two lists in sync.这种技术的代价是两次写入枚举元素并保持两个列表同步。

You can try and define the following macro:您可以尝试定义以下宏:

#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))

Now you can use it:现在你可以使用它了:

enum Count { zero, one, two, three }; 

    for_range (Count, c, zero, three)
    {
        cout << "forward: " << c << endl;
    }

It can be used to iterate backwards and forwards through unsigned, integers, enums and chars:它可用于通过无符号、整数、枚举和字符来向后和向前迭代:

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}


for_range (char, c, 'z','a')
{
    cout << c << endl;
}

Despite its awkward definition it is optimized very well.尽管它的定义很尴尬,但它的优化非常好。 I looked at disassembler in VC++.我查看了 VC++ 中的反汇编程序。 The code is extremely efficient.该代码非常有效。 Don't be put off but the three for statements: the compiler will produce only one loop after optimization!三个for语句不要被推迟:编译器在优化后只会产生一个循环! You can even define enclosed loops:您甚至可以定义封闭循环:

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;
    }

You obviously cannot iterate through enumerated types with gaps.您显然不能遍历有间隙的枚举类型。

您还可以为枚举类型重载递增/递减运算符。

Here's another solution which only works for contiguous enums.这是另一种仅适用于连续枚举的解决方案。 It gives the expected iteration, except for ugliness in the increment, which is where it belongs, since that's what's broken in C++.它给出了预期的迭代,除了增量中的丑陋,这是它所属的地方,因为这在 C++ 中被破坏了。

enum Bar {
    One = 1,
    Two,
    Three,
    End_Bar // Marker for end of enum; 
};

for (Bar foo = One; foo < End_Bar; foo = Bar(foo + 1))
{
    // ...
}

If you do not like to pollute you enum with a final COUNT item (because maybe if you also use the enum in a switch then then the compiler will warn you of a missing case COUNT:), you can do this:如果你不喜欢用最终的 COUNT 项污染你的枚举(因为也许如果你也在开关中使用枚举,那么编译器会警告你缺少 case 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);
}

In Bjarne Stroustrup's C++ programming language book, you can read that he's proposing to overload the operator++ for your specific enum .在 Bjarne Stroustrup 的 C++ 编程语言书中,您可以读到他建议为您的特定enum重载operator++ enum are user-defined types and overloading operator exists in the language for these specific situations. enum是用户定义的类型,语言中存在针对这些特定情况的重载运算符。

You'll be able to code the following:您将能够编写以下代码:

#include <iostream>
enum class Colors{red, green, blue};
Colors& operator++(Colors &c, int)
{
     switch(c)
     {
           case Colors::red:
               return c=Colors::green;
           case Colors::green:
               return c=Colors::blue;
           case Colors::blue:
               return c=Colors::red; // managing overflow
           default:
               throw std::exception(); // or do anything else to manage the error...
     }
}

int main()
{
    Colors c = Colors::red;
    // casting in int just for convenience of output. 
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    return 0;
}

test code: http://cpp.sh/357gb测试代码: http ://cpp.sh/357gb

Mind that I'm using enum class .请注意,我正在使用enum class Code works fine with enum also.代码也适用于enum But I prefer enum class since they are strong typed and can prevent us to make mistake at compile time.但我更喜欢enum class ,因为它们是强类型的,可以防止我们在编译时出错。

There is already discussion about std::initializer_list (C++11) in the comments.评论中已经讨论了 std::initializer_list (C++11)。 I am mentioning example to iterate over the enum.我提到了迭代枚举的示例。

or std::initializer_list and a simpler syntax:或 std::initializer_list 和更简单的语法:

enum E {
    E1 = 4,
    E2 = 8,
    // ..
    En
};

constexpr std::initializer_list<E> all_E = {E1, E2, /*..*/ En};

and then接着

for (auto e : all_E) {
    // Do job with e
}

Reference Link参考链接

Here are some very readable and easy-to-understand approaches, for both weakly-typed C and C++ regular enum s, and strongly-typed C++ enum class es.以下是一些非常易读且易于理解的方法,适用于弱类型C 和 C++ 常规enum以及强类型C++ enum class

I recommend compiling all examples below with -Wall -Wextra -Werror .我建议使用-Wall -Wextra -Werror编译以下所有示例。 This gives you the added safety that if you forget to cover any enum value in the switch case your compiler will throw a compile-time error !这为您提供了额外的安全性,如果您忘记在switch case 中覆盖任何枚举值,您的编译器将抛出编译时错误 This forces you to keep your enum definition and switch cases in-sync, which is an extra safety measure for your code.这迫使您保持枚举定义和切换案例同步,这是您的代码的额外安全措施。 This tip works so long as you:只要您:

  1. Cover all enum values in your switch case, and覆盖switch盒中的所有枚举值,并且
  2. Do NOT have a default switch case.没有default的开关盒。
  3. Build with the -Wall -Wextra -Werror flags.使用-Wall -Wextra -Werror标志构建。

I recommend you follow all 3 of those points, as it is a good practice and creates better code.我建议您遵循所有这 3 点,因为这是一种很好的做法并且可以创建更好的代码。

1. For a standard, weakly-typed C or C++ enum : 1. 对于标准的弱类型C 或 C++ enum

C definition (this is also valid C++): C 定义(这也是有效的 C++):

typedef enum my_error_type_e 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
} my_error_type_t;

C++ definition: C++ 定义:

enum my_error_type_t 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
};

C or C++ iteration over this weakly-typed enum:在这个弱类型枚举上的C 或 C++ 迭代:

Note: incrementing an enum by doing my_error_type++ is not allowed--not even on C-style enums, so we must do this instead: my_error_type = (my_error_type_t)(my_error_type + 1) .注意:不允许通过执行my_error_type++来增加枚举——甚至在 C 风格的枚举上也不行,所以我们必须这样做: my_error_type = (my_error_type_t)(my_error_type + 1) Notice that my_error_type + 1 is allowed, however, since this weak enum is automatically implicitly cast to an int here to make this addition possible withOUT having to manually cast it to an int like this: my_error_type = (my_error_type_t)((int)my_error_type + 1) .但是请注意, my_error_type + 1允许的,因为这个弱枚举在此处自动隐式转换为int以使添加成为可能,而不必手动将其转换为 int,如下所示: my_error_type = (my_error_type_t)((int)my_error_type + 1)

for (my_error_type_t my_error_type = MY_ERROR_TYPE_begin; 
        my_error_type < MY_ERROR_TYPE_end;
        my_error_type = (my_error_type_t)(my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case MY_ERROR_TYPE_SOMETHING_1:
            break;
        case MY_ERROR_TYPE_SOMETHING_2:
            break;
        case MY_ERROR_TYPE_SOMETHING_3:
            break;
        case MY_ERROR_TYPE_SOMETHING_4:
            break;
        case MY_ERROR_TYPE_SOMETHING_5:
            break;
        case MY_ERROR_TYPE_count:
            // This case will never be reached.
            break;
    }
}

2. For a scoped , strongly-typed C++ enum class : 2. 对于作用域强类型C++ enum class

C++ definition: C++ 定义:

enum class my_error_type_t
{
    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,
};

C++ iteration over this strongly-typed enum:在这个强类型枚举上的 C++ 迭代:

Notice the extra (size_t) cast (or (int) would be acceptable too) required to forcefully increment the enum class variable!请注意强制增加enum class变量所需的额外(size_t)强制转换(或(int)也可以接受)! I also chose to use the C++-style static_cast<my_error_type_t> cast here, but a C-style (my_error_type_t) cast, as done above, would have been fine as well.我还选择在这里使用 C++ 风格的static_cast<my_error_type_t>演员表,但如上所述,C 风格的(my_error_type_t)演员表也可以。

for (my_error_type_t my_error_type = my_error_type_t::begin; 
        my_error_type < my_error_type_t::end;
        my_error_type = static_cast<my_error_type_t>((size_t)my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case my_error_type_t::SOMETHING_1:
            break;
        case my_error_type_t::SOMETHING_2:
            break;
        case my_error_type_t::SOMETHING_3:
            break;
        case my_error_type_t::SOMETHING_4:
            break;
        case my_error_type_t::SOMETHING_5:
            break;
        case my_error_type_t::count:
            // This case will never be reached.
            break;
    }
}

Also notice the scoping.还要注意范围。 In the C++ strongly-typed enum class I used my_error_type_t:: to access each scoped enum class member.在 C++强类型enum class中,我使用my_error_type_t::来访问每个作用域enum class成员。 But, in the C-style weakly-typed regular enum , very similar scoping can be achieved, as I demonstrated, simply be prefixing each enum member name with MY_ERROR_TYPE_ .但是,在 C 风格的弱类型常规enum中,可以实现非常相似的作用域,正如我所展示的,只需在每个enum成员名称前加上MY_ERROR_TYPE_ So, the fact that the C++ strongly-typed enum class adds scoping doesn't really add much value--it's really just a personal preference in that regard.因此,C++强类型enum class添加作用域这一事实并没有真正增加太多价值——这实际上只是个人偏好。 And the fact that the C++ strongly-typed enum class has extra type-safety also has pros and cons. C++强类型enum class具有额外的类型安全性这一事实也有利有弊。 It may help you in some cases but it definitely makes incrementing the enum and iterating over it a pain-in-the-butt, which, honestly, means it is doing its job.在某些情况下它可能会对您有所帮助,但它肯定会使增加枚举并对其进行迭代变得很痛苦,老实说,这意味着它正在完成它的工作。 By making it harder to increment the scoped enum class variable as though it was an integer, the C++ strongly-typed enum class is doing exactly what it was designed to do .通过使增加作用域enum class变量变得更加困难,就好像它是一个整数一样,C++强类型enum class正在完全按照它的设计目的进行 Whether or not you want that behavior is up to you.您是否想要这种行为取决于您。 Personally, I frequently do not want that behavior, and so it is not uncommon for me to prefer to use C-style enums even in C++.就个人而言,我经常希望这种行为,因此即使在 C++ 中,我也更喜欢使用 C 风格的枚举并不少见。

See also:也可以看看:

  1. [my answer] Is there a way to initialize a vector by index in c++11? [我的回答]有没有办法在 c++11 中按索引初始化向量?
  2. [my Q&A] What are commonly-used ways to iterate over an enum class in C++? [我的问答] 在 C++ 中迭代枚举类的常用方法是什么?
  3. My answer on some of the differences between enum class es ( strongly-typed enums) and regular enum s ( weakly-typed enums) in C++: How to automatically convert strongly typed enum into int?我对 C++ 中enum class es(强类型枚举)和常规enum s(弱类型枚举)之间的一些差异的回答: 如何自动将强类型枚举转换为 int?
  4. Some of my personal notes on the -Wall -Wextra -Werror and other build options , from my eRCaGuy_hello_world repo. 我的一些关于-Wall -Wextra -Werror和其他构建选项的个人笔记,来自我的eRCaGuy_hello_world 存储库。

For MS compilers:对于 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); 
}

Note: this is a lot less code than the simple templatized custom iterator answer.注意:这比简单的模板化自定义迭代器答案要少得多。

You can get this to work with GCC by using typeof instead of decltype , but I don't have that compiler handy at the moment to make sure it compiles.您可以通过使用typeof而不是decltype来使其与 GCC 一起使用,但我目前没有方便的编译器来确保它可以编译。

Upsides: enums can have any values you like in any order you like and it's still easy to iterate over them.优点:枚举可以按您喜欢的任何顺序具有您喜欢的任何值,并且迭代它们仍然很容易。 Names and values are defined once, in the first #define.名称和值在第一个#define 中定义一次。

Downsides: if you use this at work, you need a whole paragraph to explain it to your coworkers.缺点:如果你在工作中使用它,你需要一个完整的段落来向你的同事解释它。 And, it's annoying to have to declare memory to give your loop something to iterate over, but I don't know of a workaround that doesn't confine you to enums with adjacent values (and if the enum will always have adjacent values, the enum might not be buying you all that much anyway.)而且,必须声明内存以给循环提供一些可以迭代的内容很烦人,但我不知道有一种解决方法不会将您限制在具有相邻值的枚举(如果枚举总是具有相邻值,则无论如何,枚举可能不会给你买那么多东西。)

//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

The trick of declaring a list with an undefined macro wrapper, and then defining the wrapper differently in various situations, has a lot of applications other than this one.使用未定义的宏包装器声明列表,然后在各种情况下以不同方式定义包装器的技巧,除此之外还有很多应用程序。

Casting the variable to an int& lets you increment while keeping the type readable.将变量转换为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

(Taking Marski's answer as a big hint...) (以马尔斯基的回答作为一个很大的暗示......)

Since the body of an enum definition is the same as an initialiser list it is possible to do this without repeating the item list if we use a simple macro to write out the values:由于枚举定义的主体与初始化列表相同,因此如果我们使用简单的宏来写出值,则可以在不重复项列表的情况下执行此操作:

#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 }) {
}

Pros: Simple, no repetition, and no need to maintain annoying first/last sentinel values.优点:简单,没有重复,不需要维护烦人的第一个/最后一个哨兵值。 (Indeed I think this might be the only solution suggested so far that does not require the user to remember to update an 'end' marker when a new item is added to the list.) (实际上,我认为这可能是迄今为止建议的唯一解决方案,它不需要用户记住在将新项目添加到列表时更新“结束”标记。)

Cons: Doesn't work with scoped enums (enum class) because the initialiser list would need the scope (Item::Wolf etc).缺点:不适用于范围枚举(枚举类),因为初始化列表需要范围(Item::Wolf 等)。 Also doesn't work if you want to specify the values of the enum members rather than having them defaulted.如果您想指定枚举成员的值而不是让它们默认,也不起作用。

If you knew that the enum values were sequential, for example the Qt:Key enum, you could:如果您知道枚举值是连续的,例如 Qt:Key 枚举,您可以:

Qt::Key shortcut_key = Qt::Key_0;
for (int idx = 0; etc...) {
    ....
    if (shortcut_key <= Qt::Key_9) {
        fileMenu->addAction("abc", this, SLOT(onNewTab()),
                            QKeySequence(Qt::CTRL + shortcut_key));
        shortcut_key = (Qt::Key) (shortcut_key + 1);
    }
}

It works as expected.它按预期工作。

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]
}

Extending @Eponymous's answer: It's great, but doesn't provide a general syntax.扩展@Eponymous 的答案:很好,但不提供通用语法。 Here's what I came up with:这是我想出的:

// 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

then in your namespace:然后在您的命名空间中:

#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

then wherever you need to use it:然后在任何需要使用它的地方:

for (const auto& e : Common::AllValues<MyNamespace::MyEnum>) { ... }

so even if you've typedef'd:所以即使你已经 typedef 了:

namespace YourNS {
using E = MyNamespace::MyEnum;
} // namespace YourNS

for (const auto& e : Common::AllValues<YourNS::E>) { ... }

I can't think of anything much better, short of the actual language feature everyone looking at this page want.除了查看此页面的每个人都想要的实际语言功能之外,我想不出比这更好的了。

Future work:未来的工作:

  1. You should be able to add a constexpr function (and so a metafunction) that filters Common::AllValues<E> to provide a Common::AllDistinctValues<E> for the case of enums with repeated numerical values like enum { foo = 0, bar = 0 };您应该能够添加一个constexpr函数(以及一个元函数)来过滤Common::AllValues<E>以提供Common::AllDistinctValues<E>用于具有重复数值的枚举的情况,例如enum { foo = 0, bar = 0 }; . .
  2. I bet there's a way to use the compiler's switch -covers-all- enum -values to write allValuesArray such that it errors if the enum has added a value.我敢打赌,有一种方法可以使用编译器的switch -covers-all- enum -values 来写入allValuesArray ,这样如果枚举添加了一个值,它就会出错。

Using a lambda, I found this the best (modern) way of looping over enums.使用 lambda,我发现这是循环枚举的最佳(现代)方式。 This highly improves the abstraction.这极大地提高了抽象性。 Can even make it a template, so it's applicable to any enum.甚至可以使其成为模板,因此它适用于任何枚举。 This code neither gives you issues with clang(-tidy).此代码既不会给您带来 clang(-tidy) 的问题。

#include <functional>

/// @brief Loop over all enum values where the last enum value is the invalid one
void forEachAction(std::function<void(Enum)> &&doThis) {
    for (int value = 0; value = static_cast<int>(Enum::LastValue); ++value ) {
        doThis(static_cast<Enum>(value ));
    }
}

...

forEachAction([this](Enum value) {
    ...  // what you want to execute for every enum
});

Most solution are based on loops over the (MIN, MAX) range but overlook the fact that might be holes in the enum.大多数解决方案都基于 (MIN, MAX) 范围内的循环,但忽略了枚举中可能存在漏洞的事实。

My suggestions is:我的建议是:

        for (int i = MYTYPE_MIN; i <= MYTYPE_MAX; i++) {
            if (MYTYPE_IsValid(i)) {
                MYTYPE value = (MYTYPE)i;
                // DoStuff(value)
            }   
        }   
        

C++ 没有自省功能,因此您无法在运行时确定这种事情。

Just make an array of ints and loop over the array, but make the last element say -1 and use it for exit condition.只需创建一个整数数组并遍历该数组,但将最后一个元素设为 -1 并将其用于退出条件。

If enum is:如果枚举是:

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

then create array:然后创建数组:

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

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

This does not break down no matter the ints in the representation as long as -1 check does not collide with one of the elements of course.这不会破坏表示中的整数,只要 -1 检查当然不与其中一个元素发生冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM