Wie kann ich über eine Aufzählung iterieren?

Wie kann ich über eine Aufzählung iterieren?

Der typische Weg ist wie folgt:

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

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

Bitte beachten Sie die Aufzählung Last soll von der Iteration übersprungen werden. Verwenden dieses "falschen" Last enum müssen Sie Ihre Beendigungsbedingung in der for-Schleife nicht jedes Mal auf die letzte "echte" Aufzählung aktualisieren, wenn Sie eine neue Aufzählung hinzufügen möchten. Wenn Sie später weitere Aufzählungen hinzufügen möchten, fügen Sie sie einfach vor Last hinzu. Die Schleife in diesem Beispiel funktioniert immer noch.

Dies bricht natürlich zusammen, wenn die Enum-Werte angegeben werden:

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

Dies zeigt, dass eine Aufzählung nicht wirklich zum Durchlaufen gedacht ist. Der typische Weg, mit einer Aufzählung umzugehen, besteht darin, sie in einer switch-Anweisung zu verwenden.

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

Wenn Sie wirklich aufzählen möchten, füllen Sie die Aufzählungswerte in einen Vektor und iterieren Sie darüber. Dies wird auch korrekt mit den angegebenen Aufzählungswerten umgehen.


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

Mit c++11 gibt es tatsächlich eine Alternative:das Schreiben eines einfachen vorlagenbasierten benutzerdefinierten Iterators.

Nehmen wir an, Ihre Aufzählung ist

enum class foo {
  one,
  two,
  three
};

Dieser generische Code wird den Trick ziemlich effizient erledigen - platzieren Sie ihn in einem generischen Header, er wird Ihnen für jede Aufzählung dienen, über die Sie möglicherweise iterieren müssen:

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

Sie müssen es spezialisieren

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

Und dann können Sie mit range-for

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

Die Annahme, dass Sie keine Lücken in Ihrer Aufzählung haben, ist immer noch wahr; Es gibt keine Annahme über die Anzahl der Bits, die tatsächlich benötigt werden, um den Enum-Wert zu speichern (dank std::underlying_type)