Comment puis-je itérer sur un enum?

je viens de remarquer que vous ne pouvez pas utiliser les opérateurs mathématiques standard sur un enum tel que ++ ou + =

alors quelle est la meilleure façon d'itérer toutes les valeurs dans un enum C++?

236
demandé sur Lightness Races in Orbit 2008-11-04 16:55:29

16 réponses

la voie typique est la suivante:

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

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

bien sûr, cela se décompose si les valeurs enum sont spécifiées:

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

Cela illustre qu'un enum n'est pas vraiment le but de parcourir. La façon typique de traiter un enum est de l'utiliser dans une instruction de commutation.

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

si vous voulez vraiment énumérer, remplissez les valeurs enum dans un vecteur et itérez-les. Cela permettra de traiter correctement l' les valeurs enum spécifiées aussi.

216
répondu andreas buykx 2012-02-06 19:37:45
#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;
}
22
répondu ZDF 2014-11-13 14:45:45

si votre enum commence par 0 et que l'incrément est toujours 1.

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

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

sinon je suppose que la seule raison est de créer quelque chose comme un

vector<enumType> vEnums;

ajouter les éléments et utiliser des itérateurs normaux....

18
répondu João Augusto 2008-11-04 14:20:35

avec c++11, Il y a en fait une alternative: écrire un simple itérateur personnalisé templatisé.

supposons que votre énum est

enum class foo {
  one,
  two,
  three
};

ce code générique fera l'affaire, assez efficacement-place dans un en-tête générique, il vous servira pour tout enum que vous pourriez avoir besoin d'itérer sur:

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

vous aurez besoin de le spécialiser

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

et ensuite vous pouvez itérer en utilisant la gamme-for

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

l'hypothèse que vous n'avez pas de lacunes dans votre enum est toujours vraie; il n'y a pas d'hypothèse sur le nombre de bits réellement nécessaires pour stocker la valeur d'enum (grâce à std:: underlying_type)

11
répondu Francesco Chemolli 2016-01-26 23:37:16

trop compliqué cette solution, je fais comme ça:

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

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

for (NodePosition pos : NodePositionVector) {
...
}
9
répondu Enzojz 2014-07-27 15:56:21

vous ne pouvez pas avec un enum. Peut-être qu'un enum n'est pas le meilleur pour votre situation.

une convention courante est de nommer la dernière valeur enum quelque chose comme MAX et de l'utiliser pour contrôler une boucle en utilisant un int.

8
répondu Corey Trager 2008-11-04 14:10:23

vous pouvez essayer de définir la macro suivante:

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

Maintenant vous pouvez l'utiliser:

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

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

il peut être utilisé pour itérer en arrière et en avant par des entiers, des énums et des caractères non signés:

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


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

malgré sa définition maladroite il est très bien optimisé. J'ai regardé disassembler en VC++. Le code est extrêmement efficace. Ne soyez pas rebutés, mais les trois états: le compilateur produira une seule boucle après l'optimisation! Vous pouvez même définir des boucles fermées:

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

vous ne pouvez évidemment pas itérer à travers les types énumérés avec des lacunes.

6
répondu Mikhail Semenov 2011-12-09 22:04:07

quelque chose qui n'a pas été couvert dans les autres réponses = si vous utilisez des enums C++11 fortement dactylographiés, vous ne pouvez pas utiliser ++ ou + int sur eux. Dans ce cas, un peu de solution de messier est nécessaire:

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)

}
5
répondu Riot 2013-06-13 01:15:40

vous pouvez également surcharger les opérateurs d'incrément/décrément pour votre type énuméré.

4
répondu JohnMcG 2008-11-04 17:35:39

si vous n'aimez pas polluer l'enum avec un élément de comptage final (parce que peut-être si vous utilisez aussi l'enum dans un commutateur alors le compilateur vous avertira d'un nombre de cas manquant:), vous pouvez faire ceci:

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);
}
2
répondu Niels Holst 2016-05-30 07:32:46

pour les compilateurs 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: c'est beaucoup moins de code que la simple réponse itératrice personnalisée.

vous pouvez obtenir cela pour travailler avec GCC en utilisant typeof au lieu de decltype , mais je n'ai pas ce compilateur à portée de main pour le moment pour s'assurer qu'il compile.

1
répondu user2407277 2016-02-13 23:56:38

je fais souvent ça comme ça

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

ou, si elle n'est pas successive, mais avec un pas régulier (p.ex. des indicateurs 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))
    {}
1
répondu Niki 2017-01-18 11:54:09

L'une des réponses dit:"Si vous saviez que les valeurs enum étaient séquentielles, par exemple L'enum Qt:Key".

Qt:: les valeurs clés ne sont pas séquentielles . Certains segments de l'enum.

ce fil est sur itération sur toutes les valeurs en enum. Cela est en fait possible dans Qt en raison de son utilisation 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);
}

Voir aussi QObject::metaObject() et q_enum macro.

je pense que ce genre de choses va devenir plus facile avec C++20? Mais je n'ai pas regardé.

1
répondu gatis paeglis 2018-01-12 11:45:06

C++ n'a pas d'introspection, donc vous ne pouvez pas déterminer ce genre de chose à l'exécution.

0
répondu David Kemp 2008-11-04 14:12:42

si vous saviez que les valeurs enum étaient séquentielles, par exemple L'enum Qt: Key, vous pourriez:

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

ça marche comme prévu.

0
répondu kcrossen 2016-07-31 13:58:29

il suffit de faire un tableau des ints et boucle sur le tableau, mais faites le dernier élément dire -1 et utilisez-le pour la condition de sortie.

si enum est:

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

crée alors array:

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

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

cela ne se décompose pas quelle que soit l'entrée dans la représentation aussi longtemps que -1 check ne se heurte pas à l'un des éléments bien sûr.

-1
répondu mathreadler 2018-03-15 09:51:07