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 ?

6voto

Corey Trager Punkte 21897

Bei einer Aufzählung geht das nicht. Vielleicht ist eine Enum nicht die beste Lösung für Ihre Situation.

Eine gängige Konvention ist es, den letzten Enum-Wert wie MAX zu benennen und diesen zur Steuerung einer Schleife mit einem int zu verwenden.

4voto

Mikhail Semenov Punkte 953

Sie können versuchen, das folgende Makro zu definieren:

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

Jetzt können Sie es verwenden:

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

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

Sie kann verwendet werden, um vorwärts und rückwärts durch vorzeichenlose Zahlen, Ganzzahlen, Enums und Zeichen zu iterieren:

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

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

Trotz seiner umständlichen Definition ist er sehr gut optimiert. Ich habe mir den Disassembler in VC++ angesehen. Der Code ist extrem effizient. Lassen Sie sich nicht von den drei for-Anweisungen abschrecken: Der Compiler erzeugt nach der Optimierung nur eine einzige Schleife! Sie können sogar eingeschlossene Schleifen definieren:

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

Sie können natürlich nicht durch Aufzählungstypen mit Lücken iterieren.

3voto

JohnMcG Punkte 8423

Sie können auch die Inkrement-/Dekrement-Operatoren für Ihren Aufzählungstyp überladen.

3voto

Gabriel Staples Punkte 20228

Hier sind einige sehr lesenswerte und leicht verständliche Ansätze, sowohl für schwach getippt C und C++ regulär enum s, et stark getippt C++ enum class es.

Ich empfehle die Kompilierung aller folgenden Beispiele mit -Wall -Wextra -Werror . Dies gibt Ihnen die zusätzliche Sicherheit, dass Sie, wenn Sie vergessen, einen Enum-Wert in der switch Fall wird Ihr Compiler einen Kompilierfehler auslösen ! Dies zwingt Sie dazu, Ihre Enum-Definition und die Switch-Cases synchron zu halten, was eine zusätzliche Sicherheitsmaßnahme für Ihren Code darstellt. Dieser Tipp funktioniert so lange, wie Sie:

  1. Abdeckung alle Enum-Werte in Ihrem switch Fall, und
  2. Haben Sie NICHT eine default Fall wechseln.
  3. Bauen Sie mit dem -Wall -Wextra -Werror Flaggen.

Ich empfehle Ihnen, alle 3 Punkte zu befolgen, da dies eine gute Praxis ist und besseren Code erzeugt.

1. Für einen Standard, schwach getippt C oder C++ enum :

C-Definition (dies gilt auch für C++):

typedef enum my_error_type_e 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
} my_error_type_t;

C++ Definition:

enum my_error_type_t 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
};

C oder C++-Iteration über diese Schwach getippt enum:

Hinweis: Das Inkrementieren einer Aufzählung durch my_error_type++ es no erlaubt - nicht einmal bei Enums im C-Stil, also müssen wir dies stattdessen tun: my_error_type = (my_error_type_t)(my_error_type + 1) . Beachten Sie, dass my_error_type + 1 ist erlaubt, da diese schwache Aufzählung automatisch in eine int hier, um diesen Zusatz zu ermöglichen, ohne dass er manuell in einen int wie diesen umgewandelt werden muss: my_error_type = (my_error_type_t)((int)my_error_type + 1) .

for (my_error_type_t my_error_type = MY_ERROR_TYPE_begin; 
        my_error_type < MY_ERROR_TYPE_end;
        my_error_type = (my_error_type_t)(my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case MY_ERROR_TYPE_SOMETHING_1:
            break;
        case MY_ERROR_TYPE_SOMETHING_2:
            break;
        case MY_ERROR_TYPE_SOMETHING_3:
            break;
        case MY_ERROR_TYPE_SOMETHING_4:
            break;
        case MY_ERROR_TYPE_SOMETHING_5:
            break;
        case MY_ERROR_TYPE_count:
            // This case will never be reached.
            break;
    }
}

2. Für eine skaliert , stark getippt C++ enum class :

C++ Definition:

enum class my_error_type_t
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    count,
    // helpers for iterating over the enum
    begin = 0,
    end = count,
};

C++-Iteration über diese stark getippt enum:

Beachten Sie die zusätzliche (size_t) Besetzung (oder (int) wäre auch akzeptabel) erforderlich, um die enum class variabel! Ich habe mich auch für die Verwendung der C++-ähnlichen static_cast<my_error_type_t> hier, sondern ein C-Stil (my_error_type_t) Guss, wie oben beschrieben, wäre auch in Ordnung gewesen.

for (my_error_type_t my_error_type = my_error_type_t::begin; 
        my_error_type < my_error_type_t::end;
        my_error_type = static_cast<my_error_type_t>((size_t)my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case my_error_type_t::SOMETHING_1:
            break;
        case my_error_type_t::SOMETHING_2:
            break;
        case my_error_type_t::SOMETHING_3:
            break;
        case my_error_type_t::SOMETHING_4:
            break;
        case my_error_type_t::SOMETHING_5:
            break;
        case my_error_type_t::count:
            // This case will never be reached.
            break;
    }
}

Beachten Sie auch das Scoping. In der C++ stark getippt enum class Ich habe my_error_type_t:: für den Zugriff auf jede scoped enum class Mitglied. Aber im C-Stil schwach getippt regelmäßig enum kann, wie ich gezeigt habe, eine sehr ähnliche Einteilung erreicht werden, indem man einfach jedem enum Mitgliedsname mit MY_ERROR_TYPE_ . Die Tatsache, dass die C++ stark getippt enum class Der Zusatz "Scoping" bringt keinen wirklichen Mehrwert - es ist wirklich nur eine persönliche Vorliebe in dieser Hinsicht. Und die Tatsache, dass die C++ stark getippt enum class hat eine zusätzliche Typensicherheit, die Vor- und Nachteile hat. Es kann Ihnen in einigen Fällen helfen, aber es macht auf jeden Fall die Inkrementierung der Aufzählung und Iteration über sie ein pain-in-the-butt, die, ehrlich gesagt, bedeutet es seine Arbeit tut. Indem man es härter zur Erhöhung der skalierten enum class Variable wie eine Ganzzahl, die C++ stark getippt enum class tut genau das, wofür es entwickelt wurde . Ob Sie nun wollen Dieses Verhalten ist Ihnen überlassen. Ich persönlich mache häufig no wollen dieses Verhalten, und so ist es nicht ungewöhnlich, dass ich es vorziehe, Enums im Stil von C auch in C++ zu verwenden.

Siehe auch:

  1. (meine Antwort) Gibt es eine Möglichkeit, einen Vektor durch Index in C++11 zu initialisieren?
  2. [meine Fragen und Antworten] Was sind gängige Methoden, um über eine Enum-Klasse in C++ zu iterieren?
  3. Meine Antwort auf einige der Unterschiede zwischen enum class es ( stark getippt Enums) und reguläre enum s ( schwach getippt enums) in C++: Wie konvertiert man automatisch stark typisierte enum in int?
  4. Einige meiner persönlichen Notizen zum -Wall -Wextra -Werror und andere Bauoptionen von meinem eRCaGuy_hello_world Repo.

3voto

Ethan Bradford Punkte 630

Hier ist eine andere Lösung, die nur für zusammenhängende Enums funktioniert. Es gibt die erwartete Iteration, mit Ausnahme der Hässlichkeit im Inkrement, wo es hingehört, denn das ist, was in C++ kaputt ist.

enum Bar {
    One = 1,
    Two,
    Three,
    End_Bar // Marker for end of enum; 
};

for (Bar foo = One; foo < End_Bar; foo = Bar(foo + 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