429 Stimmen

Wie kann ich über eine Aufzählung iterieren?

Mir ist gerade aufgefallen, dass man die Standard-Mathematik-Operatoren nicht auf eine enum wie zum Beispiel ++ o += .

Wie kann man also am besten durch alle Werte in einer C++-Datei iterieren? enum ?

330voto

andreas buykx Punkte 12236

Die typische Vorgehensweise 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, dass das Enum Last soll bei der Iteration übersprungen werden. Die Verwendung dieser "Fälschung" Last enum, müssen Sie nicht jedes Mal, wenn Sie eine neue enum hinzufügen wollen, Ihre Abbruchbedingung in der for-Schleife auf die letzte "echte" enum aktualisieren. Wenn Sie später weitere Enums hinzufügen möchten, fügen Sie sie einfach vor Last ein. Die Schleife in diesem Beispiel funktioniert trotzdem.

Das funktioniert natürlich nicht, wenn die Enum-Werte angegeben werden:

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

Dies verdeutlicht, dass eine Aufzählung nicht wirklich dazu gedacht ist, sie zu durchlaufen. Die typische Art, mit einer Aufzählung umzugehen, ist die Verwendung in einer switch-Anweisung.

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 wollen, stopfen Sie die Aufzählungswerte in einen Vektor und iterieren Sie über diesen. Dadurch werden auch die angegebenen Enum-Werte korrekt behandelt.

90voto

zdf Punkte 3795
#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;
}

47voto

Mit C++11 gibt es tatsächlich eine Alternative: das Schreiben eines einfachen benutzerdefinierten Iterators mit Vorlage.

Nehmen wir an, Ihre Aufzählung lautet

enum class foo {
  one,
  two,
  three
};

Dieser generische Code wird den Trick tun, ziemlich effizient - Platz in einem generischen Header, wird es Ihnen für jede Enum Sie brauchen, um über iterate dienen:

#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 kann man 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 Aufzählungswert zu speichern (dank std::underlying_type)

31voto

Enzojz Punkte 797

Zu viel kompliziert diese Lösung, ich mag das:

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

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

for (NodePosition pos : NodePositionVector) {
...
}

22voto

Niki Punkte 491

Ich mache das oft so

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

oder wenn nicht sukzessive, sondern mit regelmäßigem Schritt (z. B. Bit-Flags)

    enum EAnimalCaps
    {
        E_None    = 0,
        E_First   = 0x1,
        E_CanFly  = E_First,
        E_CanWalk = 0x2
        E_CanSwim = 0x4,
        E_Last
    }

    class MyAnimal
    {
       EAnimalCaps m_Caps;
    }

    class Frog
    {
        Frog() : 
            m_Caps(EAnimalCaps(E_CanWalk | E_CanSwim))
        {}
    }

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X