¿Cómo puedo iterar sobre una enumeración?

Me di cuenta de que no puedes usar operadores matemáticos estándar en una enumeración como ++ o + =

Entonces, ¿cuál es la mejor manera de recorrer todos los valores en una enumeración de C ++?

La forma típica es la siguiente:

 enum Foo { One, Two, Three, Last }; for ( int fooInt = One; fooInt != Last; fooInt++ ) { Foo foo = static_cast(fooInt); // ... } 

Por supuesto, esto se rompe si se especifican los valores enum:

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

Esto ilustra que, en realidad, una enumeración no debe repetirse. La forma típica de tratar con una enumeración es usarla en una statement de cambio.

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

Si realmente desea enumerar, rellene los valores enum en un vector e itere sobre eso. Esto también tratará adecuadamente con los valores enum especificados.

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

Si su enumeración comienza con 0 y el incremento siempre es 1.

 enum enumType { A = 0, B, C, enumTypeEnd }; for(int i=0; i 

Si no, supongo que el único motivo es crear algo así como

 vector vEnums; 

agrega los elementos y usa iteradores normales ....

Con c ++ 11, en realidad hay una alternativa: escribir un iterador personalizado con plantilla simple.

supongamos que tu enum es

 enum class foo { one, two, three }; 

Este código genérico hará el truco, bastante eficiente: coloque en un encabezado genérico, le servirá para cualquier enumeración que necesite repetir:

 #include  template < typename C, C beginVal, C endVal> class Iterator { typedef typename std::underlying_type::type val_t; int val; public: Iterator(const C & f) : val(static_cast(f)) {} Iterator() : val(static_cast(beginVal)) {} Iterator operator++() { ++val; return *this; } C operator*() { return static_cast(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; } }; 

Tendrás que especializarlo

 typedef Iterator fooIterator; 

Y luego puedes iterar usando range-for

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

La suposición de que no tienes vacíos en tu enumeración sigue siendo cierta; no hay ninguna suposición sobre la cantidad de bits realmente necesarios para almacenar el valor enum (gracias a std :: tipo_incrustado)

Demasiado complicado esta solución, me gusta eso:

 enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3}; const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary }; for (NodePosition pos : NodePositionVector) { ... } 

No puedes con una enumeración. Tal vez una enumeración no es la mejor opción para su situación.

Una convención común es nombrar el último valor enum algo como MAX y usar eso para controlar un bucle usando un int.

Puede intentar y definir la siguiente 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)) 

Ahora puedes usarlo:

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

Se puede usar para iterar hacia atrás y hacia adelante a través de enteros, enteros, y caracteres sin signo:

 for_range (unsigned, i, 10,0) { cout << "backwards i: " << i << endl; } for_range (char, c, 'z','a') { cout << c << endl; } 

A pesar de su definición incómoda, está optimizado muy bien. Miré desensamblador en VC ++. El código es extremadamente eficiente. No se desanime, pero las tres declaraciones: el comstackdor producirá solo un ciclo después de la optimización. Incluso puede definir bucles cerrados:

 unsigned p[4][5]; for_range (Count, i, zero,three) for_range(unsigned int, j, 4, 0) { p[i][j] = static_cast(i)+j; } 

Obviamente no puede iterar a través de tipos enumerados con espacios.

Algo que no se ha cubierto en las otras respuestas = si está utilizando enums de C ++ 11 fuertemente tipados, no puede usar ++ o + int en ellos. En ese caso, se requiere una solución algo más desordenada:

 enum class myenumtype { MYENUM_FIRST, MYENUM_OTHER, MYENUM_LAST } for(myenumtype myenum = myenumtype::MYENUM_FIRST; myenum != myenumtype::MYENUM_LAST; myenum = static_cast(static_cast(myenum) + 1)) { do_whatever(myenum) } 

También puede sobrecargar los operadores de incremento / decremento para su tipo enumerado.

Si no le gusta contaminar su enumeración con un COUNT final (porque quizás si también usa la enumeración en un conmutador, entonces el comstackdor le advertirá sobre un caso perdido COUNT :), puede hacer esto:

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

Para comstackdores de 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); } 

Nota: este es un código mucho menos que la simple respuesta de iterador personalizado con plantilla.

Puede hacer que esto funcione con GCC usando typeof lugar de decltype , pero no tengo ese comstackdor a mano en este momento para asegurarme de que comstack.

A menudo lo hago así

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

o si no es sucesivo, pero con paso regular (por ejemplo, indicadores de bits)

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

Una de las respuestas dice: “Si supiera que los valores enum fueron secuenciales, por ejemplo Qt: Key enum”.

Qt :: Los valores clave no son secuenciales . Algunos segmentos en la enumeración son.

Este hilo trata de iterar sobre todos los valores en enum. Esto es posible en Qt debido a su uso de Meta Object System:

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

Ver también QObject :: metaObject () y macro Q_ENUM.

Creo que este tipo de cosas será más fácil con C ++ 20? Pero no lo he investigado.

C ++ no tiene introspección, por lo que no puede determinar este tipo de cosas en tiempo de ejecución.

Si sabía que los valores enum eran secuenciales, por ejemplo Qt: Key enum, podría:

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

Funciona como se esperaba.

Simplemente haga una serie de entradas y recorra la matriz, pero haga que el último elemento diga -1 y úselo para la condición de salida.

Si enum es:

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

luego crea una matriz:

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

Esto no se rompe sin importar las entradas en la representación, siempre y cuando la verificación -1 no colisione con uno de los elementos, por supuesto.