如何迭代枚举?

时间:2022-04-30 14:30:53

I just noticed that you can not use standard math operators on an enum such as ++ or +=

我刚刚注意到,您不能在枚举中使用标准的数学运算符,比如++或+=

So what is the best way to iterate through all of the values in a C++ enum?

那么,迭代c++枚举中的所有值的最好方法是什么呢?

17 个解决方案

#1


208  

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);
   // ...
}

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.

这说明enum并不是要进行迭代。处理enum的典型方法是在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.

如果您真的想要枚举,可以将enum值填充到一个向量中,并对其进行迭代。这也将适当地处理指定的enum值。

#2


31  

One of many approaches: When enum Just Isn't Enough: Enumeration Classes for C++.

许多方法中的一种:当enum还不够时:c++的枚举类。

And, if you want something more encapsulated, try this approach from James Kanze.

如果你想要更简洁的东西,试试James Kanze的方法。

#3


18  

If your enum starts with 0 and the increment is always 1.

如果enum以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

如果不是,我猜唯一的原因就是创造出像a这样的东西

vector<enumType> vEnums;

add the items, and use normal iterators....

添加条目,使用正常的迭代器....

#4


17  

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

#5


11  

With c++11, there actually is an alternative: writing a simple templatized custom iterator.

对于c++11,实际上还有一个替代方法:编写一个简单的模板化自定义迭代器。

let's assume your enum is

假设你的enum是

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:

这个通用代码可以很有效地完成这个任务——把它放在一个通用头中,它可以为您提供任何您可能需要迭代的enum:

#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

然后你可以使用范围迭代。

for (foo i : fooIterator() ) { //notice the parenteses!
   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)

假设你在全会中没有间隙仍然是正确的;对于实际需要多少位来存储enum值没有任何假设(多亏了std::underlying_type)

#6


9  

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

#7


8  

You can't with an enum. Maybe an enum isn't the best fit for your situation.

你不能用enum。也许全会不适合你的情况。

A common convention is to name the last enum value something like MAX and use that to control a loop using an int.

一个常见的约定是将最后一个enum值命名为MAX,并使用它来使用int控制循环。

#8


6  

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++. The code is extremely efficient. Don't be put off but the three for statements: the compiler will produce only one loop after optimization! You can even define enclosed loops:

尽管它的定义很笨拙,但它优化得很好。我看了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;
    }

You obviously cannot iterate through enumerated types with gaps.

显然,您不能在有间隙的枚举类型中迭代。

#9


5  

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. In that case, a bit of a messier solution is required:

其他答案中没有涉及的内容=如果您使用强类型的c++ 11 enums,则不能在它们上使用++或+ int。在这种情况下,需要一个更混乱的解决方案:

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)

}

#10


4  

You can also overload the increment/decrement operators for your enumerated type.

您还可以重载枚举类型的递增/递减操作符。

#11


2  

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:

如果您不喜欢使用最终计数项污染enum(因为如果您也在开关中使用enum,那么编译器将警告您丢失的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);
}

#12


1  

For MS compilers:

对于编译器女士:

#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,但是我现在没有现成的编译器来确保编译成功。

#13


1  

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 (e.g. bit flags)

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

    enum EMyEnum
    {
        E_First,
        E_None = E_First,
        E_Green = 0x1,
        E_White = 0x2
        E_Blue  = 0x4,
        E_Last
    }

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

#14


1  

One of the answers says: "If you knew that the enum values were sequential, for example the Qt:Key enum".

其中一个答案是:“如果您知道enum值是连续的,例如Qt:Key enum”。

Qt::Key values are not sequential. Some segments in the enum are.

键值不是顺序的。枚举中的一些段是。

This thread is about iterating over all values in enum. This actually is possible in Qt due to its use of Meta Object System:

这个线程遍历enum中的所有值。这在Qt中是可能的,因为它使用元对象系统:

const QMetaObject *metaObject = qt_getQtMetaObject();
QMetaEnum keyEnum = metaObject->enumerator(metaObject->indexOfEnumerator("Key"));
for (int i = 0; i < keyEnum.keyCount(); ++i) {
    qDebug() << keyEnum.key(i);
}

See also QObject::metaObject() and Q_ENUM macro.

参见QObject::metaObject()和Q_ENUM宏。

I think this kind of stuff will become easier with C++20? But I haven't looked into it.

我认为c++ 20会让这类事情变得更简单?但我还没调查过。

#15


0  

C++ doesn't have introspection, so you can't determine this kind of thing at run-time.

c++没有自省功能,所以您不能在运行时确定这类内容。

#16


0  

If you knew that the enum values were sequential, for example the Qt:Key enum, you could:

如果您知道enum值是连续的,例如Qt:Key enum,您可以:

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.

它能够正常工作。

#17


-1  

Just make an array of ints and loop over the array, but make the last element say -1 and use it for exit condition.

只需创建一个ints数组并对数组进行循环,但是将最后一个元素设置为-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检查不与其中一个元素发生碰撞,无论表示中的ints如何,它都不会崩溃。

#1


208  

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);
   // ...
}

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.

这说明enum并不是要进行迭代。处理enum的典型方法是在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.

如果您真的想要枚举,可以将enum值填充到一个向量中,并对其进行迭代。这也将适当地处理指定的enum值。

#2


31  

One of many approaches: When enum Just Isn't Enough: Enumeration Classes for C++.

许多方法中的一种:当enum还不够时:c++的枚举类。

And, if you want something more encapsulated, try this approach from James Kanze.

如果你想要更简洁的东西,试试James Kanze的方法。

#3


18  

If your enum starts with 0 and the increment is always 1.

如果enum以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

如果不是,我猜唯一的原因就是创造出像a这样的东西

vector<enumType> vEnums;

add the items, and use normal iterators....

添加条目,使用正常的迭代器....

#4


17  

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

#5


11  

With c++11, there actually is an alternative: writing a simple templatized custom iterator.

对于c++11,实际上还有一个替代方法:编写一个简单的模板化自定义迭代器。

let's assume your enum is

假设你的enum是

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:

这个通用代码可以很有效地完成这个任务——把它放在一个通用头中,它可以为您提供任何您可能需要迭代的enum:

#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

然后你可以使用范围迭代。

for (foo i : fooIterator() ) { //notice the parenteses!
   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)

假设你在全会中没有间隙仍然是正确的;对于实际需要多少位来存储enum值没有任何假设(多亏了std::underlying_type)

#6


9  

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

#7


8  

You can't with an enum. Maybe an enum isn't the best fit for your situation.

你不能用enum。也许全会不适合你的情况。

A common convention is to name the last enum value something like MAX and use that to control a loop using an int.

一个常见的约定是将最后一个enum值命名为MAX,并使用它来使用int控制循环。

#8


6  

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++. The code is extremely efficient. Don't be put off but the three for statements: the compiler will produce only one loop after optimization! You can even define enclosed loops:

尽管它的定义很笨拙,但它优化得很好。我看了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;
    }

You obviously cannot iterate through enumerated types with gaps.

显然,您不能在有间隙的枚举类型中迭代。

#9


5  

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. In that case, a bit of a messier solution is required:

其他答案中没有涉及的内容=如果您使用强类型的c++ 11 enums,则不能在它们上使用++或+ int。在这种情况下,需要一个更混乱的解决方案:

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)

}

#10


4  

You can also overload the increment/decrement operators for your enumerated type.

您还可以重载枚举类型的递增/递减操作符。

#11


2  

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:

如果您不喜欢使用最终计数项污染enum(因为如果您也在开关中使用enum,那么编译器将警告您丢失的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);
}

#12


1  

For MS compilers:

对于编译器女士:

#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,但是我现在没有现成的编译器来确保编译成功。

#13


1  

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 (e.g. bit flags)

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

    enum EMyEnum
    {
        E_First,
        E_None = E_First,
        E_Green = 0x1,
        E_White = 0x2
        E_Blue  = 0x4,
        E_Last
    }

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

#14


1  

One of the answers says: "If you knew that the enum values were sequential, for example the Qt:Key enum".

其中一个答案是:“如果您知道enum值是连续的,例如Qt:Key enum”。

Qt::Key values are not sequential. Some segments in the enum are.

键值不是顺序的。枚举中的一些段是。

This thread is about iterating over all values in enum. This actually is possible in Qt due to its use of Meta Object System:

这个线程遍历enum中的所有值。这在Qt中是可能的,因为它使用元对象系统:

const QMetaObject *metaObject = qt_getQtMetaObject();
QMetaEnum keyEnum = metaObject->enumerator(metaObject->indexOfEnumerator("Key"));
for (int i = 0; i < keyEnum.keyCount(); ++i) {
    qDebug() << keyEnum.key(i);
}

See also QObject::metaObject() and Q_ENUM macro.

参见QObject::metaObject()和Q_ENUM宏。

I think this kind of stuff will become easier with C++20? But I haven't looked into it.

我认为c++ 20会让这类事情变得更简单?但我还没调查过。

#15


0  

C++ doesn't have introspection, so you can't determine this kind of thing at run-time.

c++没有自省功能,所以您不能在运行时确定这类内容。

#16


0  

If you knew that the enum values were sequential, for example the Qt:Key enum, you could:

如果您知道enum值是连续的,例如Qt:Key enum,您可以:

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.

它能够正常工作。

#17


-1  

Just make an array of ints and loop over the array, but make the last element say -1 and use it for exit condition.

只需创建一个ints数组并对数组进行循环,但是将最后一个元素设置为-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检查不与其中一个元素发生碰撞,无论表示中的ints如何,它都不会崩溃。