(Wie) kann ich die Elemente in einer Aufzählung zählen?

98

Diese Frage kam mir in den Sinn, als ich so etwas hatte

enum Folders {FA, FB, FC};

und wollte ein Array von Containern für jeden Ordner erstellen:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(Verwenden von Karten ist es viel eleganter zu verwenden: std::map<Folders, ContainerClass*> m_containers;)

Um jedoch auf meine ursprüngliche Frage zurückzukommen: Was gibt es eine Möglichkeit, um herauszufinden, wie viele Elemente sich in Ordnern befinden, wenn ich die Arraygröße nicht fest codieren möchte? (Ohne sich darauf zu verlassen, dass z. B. FCdas letzte Element in der Liste ist, das so etwas zulässt, ContainerClass*m_containers[FC+1]wenn ich mich nicht irre.)

fuenfundachtzig
quelle
Dieser Beitrag kann Ihre Frage beantworten: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked
1
Die Frage ist etwas unterbestimmt. Gemäß dem C ++ - Standard int(FA) | int(FB) | int (FC)ist dies auch ein zulässiger Wert für eine FoldersVariable. Wenn Sie die Größe m_containersso festlegen , dass eine FoldersVariable ein gültiger Index ist, [FC+1]wäre sie nicht groß genug.
MSalters
Ich habe eine sehr verwandte Sache auf stackoverflow.com/questions/12972317/count-on-enum-c-automatic
Sergiol
Ich würde Ihnen die Lösung von stackoverflow.com/a/60216003/12894563 und die verbesserte Variante von stackoverflow.com/a/60271408/12894563
ixjxk

Antworten:

123

Es gibt keinen wirklich guten Weg, dies zu tun. Normalerweise sehen Sie einen zusätzlichen Gegenstand in der Aufzählung, dh

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Dann können Sie Folgendes tun:

int fuz[FOOBAR_NR_ITEMS];

Immer noch nicht sehr schön.

Aber natürlich stellen Sie fest, dass nur die Anzahl der Elemente in einer Aufzählung nicht sicher ist, z

enum foobar {foo, bar = 5, baz, quz = 20};

Die Anzahl der Elemente wäre 4, aber die ganzzahligen Werte der Aufzählungswerte würden weit außerhalb des Array-Indexbereichs liegen. Die Verwendung von Enum-Werten für die Array-Indizierung ist nicht sicher. Sie sollten andere Optionen in Betracht ziehen.

Bearbeiten: Wie gewünscht, wurde der spezielle Eintrag stärker hervorgehoben.

welche
quelle
27
Nennen Sie es LAST oder ALWAYS_AT_END oder etwas nicht so Kryptisches. Lass es herausragen . Damit nachfolgende Betreuer nicht versehentlich neue Einträge hinzufügen, nachdem Sie die Markierung beendet haben.
Martin York
3
Dies ist der Ansatz, den wir in unserer Organisation verfolgen. Wir nennen es normalerweise FINAL_enumname_ENTRY, wie z. B. FINAL_foobar_ENTRY. Ich habe auch gesehen, dass Leute eine separate statische const FOOBAR_COUNT-Variable verwenden, die direkt nach der Enum-Deklaration definiert wurde, ein Ansatz, der etwas fehleranfälliger ist.
Darryl
1
Zumindest ist es ziemlich leicht zu erkennen, dass "enum foo {a = 10, LAST}" seltsam sein wird. Und ich dachte, "int arr [LAST]" wäre in diesem Fall 11 Elemente, nicht 2, also funktioniert der meiste Code (aber Sie verschwenden Speicher für ungültige Indexwerte)
Code Abominator
32

Für C ++ stehen verschiedene typsichere Aufzählungstechniken zur Verfügung, und einige davon (wie die vorgeschlagene, aber nie übermittelte Boost.Enum ) unterstützen das Abrufen der Größe einer Aufzählung.

Der einfachste Ansatz, der sowohl in C als auch in C ++ funktioniert, besteht darin, eine Konvention zum Deklarieren eines ... MAX-Werts für jeden Ihrer Aufzählungstypen anzuwenden:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Bearbeiten : In Bezug auf { FA, FB, FC, Folders_MAX = FC}versus {FA, FB, FC, Folders_MAX]: Ich bevorzuge es aus einigen Gründen, den Wert ... MAX auf den letzten zulässigen Wert der Aufzählung zu setzen:

  1. Der Name der Konstante ist technisch genauer (da er Folders_MAXden maximal möglichen Aufzählungswert ergibt).
  2. Persönlich möchte ich mich Folders_MAX = FCvon anderen Einträgen etwas mehr abheben (was es etwas schwieriger macht, versehentlich Aufzählungswerte hinzuzufügen, ohne den Maximalwert zu aktualisieren, ein Problem, auf das Martin York verwiesen hat).
  3. GCC enthält hilfreiche Warnungen wie "Aufzählungswert nicht im Schalter enthalten" für Code wie den folgenden. Wenn Sie Folders_MAX == FC + 1 zulassen, werden diese Warnungen unterbrochen, da Sie am Ende eine Reihe von ... MAX-Aufzählungswerten erhalten, die niemals in switch enthalten sein sollten.
Schalter (Ordner) 
{
  Fall FA: ...;
  Fall FB: ...;
  // Ups, FC vergessen!
}}
Josh Kelley
quelle
3
Warum nicht : enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];?
Bill
1
Ich möchte lieber klarstellen, dass die letzte eine Zahl ist, und sie haben alle den gleichen Namen dank:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte
2
Eigentlich denke ich, dass diese "hilfreichen" Warnungen ein richtiger Schmerz im Arsch sind. Ich mag gute Warnungen, die ich bei der Entwicklung immer -Wall -pedantic usw. gesetzt habe, aber diese Warnungen sind einfach nur dumm. Es gibt nur wenige schlechtere, wie zum Beispiel Parens für && || und & ^ | Vorrang des Operators. Ich meine, ich dachte , java Babysitter Sprache war, was zum Teufel ist mit C und C ++ geschieht ...
Weicht
2
Der Nachteil von Folders_max = FC ist, dass Sie es jedes Mal ändern müssen, wenn Sie der Aufzählung etwas hinzufügen!
Étienne
2
Enum Folders {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Nur um zu betonen, dass es für die Iteration nützlich ist?
gjpc
7

Wie wäre es mit Merkmalen auf STL-Weise? Zum Beispiel:

enum Foo
{
    Bar,
    Baz
};

Schreib 'ein

std::numeric_limits<enum Foo>::max()

Spezialisierung (möglicherweise constexpr, wenn Sie c ++ 11 verwenden). Geben Sie dann in Ihrem Testcode statische Zusicherungen an, um die Einschränkungen beizubehalten, die std :: numeric_limits :: max () = last_item sind.

Wojciech Migda
quelle
2
Leider funktioniert dies gemäß dieser Antwort nicht .
rr-
3
std::numeric_limits<enum Foo>::max()Gibt immer Null zurück ... (siehe die Frage für die verknüpfte Antwort) Getestet auf normalen Aufzählungen ( enum Foo { ... }), typgesteuerten Aufzählungen ( enum class Foo : uint8_t { ... }) mit gcc 5.2.0 @ Linux und MinGW 4.9.3 @ Windows.
rr-
1
(... und im Falle von std::numeric_limits<std::underlying_type<Foo>::type>::max()gibt es den Maximalwert des zugrunde liegenden Typs zurück, dh 0xFFFFFFFF für 32-Bit-Ganzzahlen, was in diesem Zusammenhang nicht nützlich ist.)
rr-
1
Seltsam, weil ich Arbeitscode habe, der das tut, was ich beschrieben habe. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Dann: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) Und std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;druckt das erwartete Ergebnis. Getestet mit den Geschmacksrichtungen gcc 5.2.1 und 4.8 / 4.9.
Wojciech Migda
2
-1; Pingen Sie mich an, wenn Sie die Antwort ändern, damit ich die Abstimmung rückgängig machen kann. Diese Antwort ist eine schlechte Konvention. Es ist ein Missbrauch des Konzepts von numeric_limits<T>::max(). Das einzige, was diese Funktion vernünftigerweise zurückgeben könnte, ist der höchste aufgezählte Wert. Es würde zurückkehren 2, aber das OP (in diesem speziellen Fall) würde es brauchen, um zurückzukehren 3. Sobald Sie nicht standardmäßige Werte für enum ( FB = 2057) haben, sind alle Wetten deaktiviert und können nicht einmal + 1den Fehler "by-by-one" umgehen. Wenn es einen numeric_limits<T>::number_of_elements_of_the_set()(oder einen kürzeren) Namen gäbe , könnte dieser ohne Mehrdeutigkeit verwendet werden.
Merlyn Morgan-Graham
3

Fügen Sie am Ende Ihrer Aufzählung einen Eintrag mit dem Namen Folders_MAX oder ähnliches hinzu und verwenden Sie diesen Wert beim Initialisieren Ihrer Arrays.

ContainerClass* m_containers[Folders_MAX];
Kevin Doyon
quelle
2

Ich verwende gerne Aufzählungen als Argumente für meine Funktionen. Es ist ein einfaches Mittel, eine feste Liste von "Optionen" bereitzustellen. Das Problem mit der am besten bewerteten Antwort hier ist, dass ein Client damit eine "ungültige Option" angeben kann. Als Nebeneffekt empfehle ich, im Wesentlichen dasselbe zu tun, aber ein konstantes int außerhalb der Aufzählung zu verwenden, um die Anzahl von ihnen zu definieren.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

Es ist nicht angenehm, aber es ist eine saubere Lösung, wenn Sie die Aufzählung nicht ändern, ohne die Konstante zu aktualisieren.

BuvinJ
quelle
1

Ich sehe wirklich keine Möglichkeit, die Anzahl der Werte in einer Aufzählung in C ++ wirklich zu erreichen. Jede der oben genannten Lösungen funktioniert, solange Sie den Wert Ihrer Aufzählungen nicht definieren, wenn Sie einen Wert definieren, der möglicherweise auf Situationen stößt, in denen Sie entweder zu große oder zu kleine Arrays erstellen

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

In diesem Beispiel würde das Ergebnis ein Array von 3 Elementen erstellen, wenn Sie ein Array von 5 Elementen benötigen

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

In diesem Beispiel würde das Ergebnis ein Array von 301 Elementen erstellen, wenn Sie ein Array von 5 Elementen benötigen

Der beste Weg, um dieses Problem im allgemeinen Fall zu lösen, wäre, Ihre Aufzählungen zu durchlaufen, aber das ist meines Wissens noch nicht im Standard


quelle