[C++] ¿Cómo puedo iterar sobre una enumeración?


Answers

Uno de los muchos enfoques: Cuando enum Just Is not Enough: Enumeration Classes for C ++ .

Y, si quieres algo más encapsulado, prueba este enfoque de James Kanze.

Question

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 ++?




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



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.




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




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.




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 compilador 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<unsigned>(i)+j;
    }

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




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



Para compiladores 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 compilador a mano en este momento para asegurarme de que compila.