(Come) posso contare gli elementi in un'enumerazione?


98

Questa domanda mi è venuta in mente, quando ho avuto qualcosa di simile

enum Folders {FA, FB, FC};

e volevo creare una serie di contenitori per ogni cartella:

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

(Usare le mappe è molto più elegante da usare std::map<Folders, ContainerClass*> m_containers;:)

Ma per tornare alla mia domanda originale: cosa succede se non voglio codificare in modo rigido la dimensione dell'array, c'è un modo per capire quanti elementi ci sono nelle cartelle? (Senza fare affidamento, ad esempio, FCsull'essere l'ultimo elemento della lista che consentirebbe qualcosa di simile ContainerClass*m_containers[FC+1]se non mi sbaglio.)


Questo post potrebbe rispondere alla tua domanda: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked

1
La domanda è un po 'sotto specificata. Secondo lo standard C ++, int(FA) | int(FB) | int (FC)è anche un valore legale per una Foldersvariabile. Se stai ridimensionando in m_containersmodo che qualsiasi Foldersvariabile sia un indice valido, [FC+1]non sarebbe abbastanza grande.
MSalters


Mi piacerebbe raccomando la soluzione da stackoverflow.com/a/60216003/12894563 e variante migliorata da stackoverflow.com/a/60271408/12894563
ixjxk

Risposte:


123

Non c'è davvero un buon modo per farlo, di solito vedi un elemento in più nell'enumerazione, ad es

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

Quindi puoi fare:

int fuz[FOOBAR_NR_ITEMS];

Comunque non è ancora molto carino.

Ma ovviamente ti rendi conto che solo il numero di elementi in un enum non è sicuro, dato ad es

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

il numero di elementi sarebbe 4, ma i valori interi dei valori enum sarebbero fuori dall'intervallo dell'indice dell'array. L'uso dei valori enum per l'indicizzazione di array non è sicuro, dovresti considerare altre opzioni.

modifica: come richiesto, fa risaltare maggiormente la voce speciale.


27
Chiamalo LAST o ALWAYS_AT_END o qualcosa di non così criptico. Fallo risaltare . In modo che i successivi manutentori non aggiungano accidentalmente nuove voci dopo aver terminato il marker.
Martin York,

3
Nel bene e nel male, questo è l'approccio che adottiamo nella nostra organizzazione. Normalmente lo chiamiamo FINAL_enumname_ENTRY, come FINAL_foobar_ENTRY. Ho anche visto persone utilizzare una variabile const FOOBAR_COUNT separata definita subito dopo la dichiarazione enum, un approccio leggermente più soggetto a errori.
Darryl

1
Almeno è abbastanza facile vedere che "enum foo {a = 10, LAST}" sarà strano. E ho pensato che "int arr [LAST]" sarebbe stato 11 elementi in questo caso, non 2, quindi la maggior parte del codice funzionerà (ma stai sprecando memoria su valori di indice non validi)
Code Abominator

32

Per C ++, sono disponibili varie tecniche enum indipendenti dai tipi e alcune di esse (come Boost.Enum proposto ma mai inviato ) includono il supporto per ottenere le dimensioni di un'enumerazione.

L'approccio più semplice, che funziona sia in C che in C ++, è quello di adottare una convenzione per dichiarare un valore ... MAX per ciascuno dei tipi di enumerazione:

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

Modifica : per quanto riguarda { FA, FB, FC, Folders_MAX = FC}versus {FA, FB, FC, Folders_MAX]: preferisco impostare il ... valore MAX sull'ultimo valore legale dell'enumerazione per alcuni motivi:

  1. Il nome della costante è tecnicamente più accurato (poiché Folders_MAXfornisce il massimo valore di enum possibile).
  2. Personalmente, mi sento come Folders_MAX = FC si distinguesse un po 'di più dalle altre voci (rendendo un po' più difficile aggiungere accidentalmente valori enum senza aggiornare il valore massimo, un problema a cui fa riferimento Martin York).
  3. GCC include utili avvisi come "valore di enumerazione non incluso nello switch" per codice come il seguente. Lasciando Folders_MAX == FC + 1 interrompe questi avvertimenti, poiché si finisce con un mucchio di ... MAX valori di enumerazione che non dovrebbero mai essere inclusi in switch.
switch (cartella) 
{
  caso FA: ...;
  caso FB: ...;
  // Oops, ho dimenticato FC!
}

3
Perché non fare enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Bill

1
Preferisco chiarire che l'ultimo è un numero, e hanno tutti lo stesso nome grazie a:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte

2
In realtà penso che quegli avvertimenti "utili" siano un vero rompicoglioni. Mi piacciono i buoni avvertimenti che imposto sempre -Wall -pedantic, ecc. Quando sto sviluppando, ma questi avvertimenti sono semplicemente stupidi. Ce ne sono solo alcuni peggiori, come suggerire parentesi per && || e & ^ | precedenza degli operatori. Voglio dire, pensavo che java fosse il linguaggio della babysitter, cosa diavolo sta succedendo a C e C ++ ...
che il

2
Lo svantaggio di avere Folders_max = FC è che devi cambiarlo ogni volta che aggiungi qualcosa all'enumerazione!
Étienne

2
enumerare le cartelle {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Giusto per sottolineare che è utile per l'iterazione?
gjpc

7

Che ne dici dei tratti, in modo STL? Per esempio:

enum Foo
{
    Bar,
    Baz
};

Scrivi un

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

specializzazione (possibilmente constexpr se usi c ++ 11). Quindi, nel codice di test, fornire eventuali asserzioni statiche per mantenere i vincoli che std :: numeric_limits :: max () = last_item.


2
Sfortunatamente questo non funzionerà secondo questa risposta .
rr-

3
std::numeric_limits<enum Foo>::max()restituisce sempre zero ... (vedere la domanda per la risposta collegata) Testato su enumerazioni normali ( enum Foo { ... }), enumerazioni suggerite dal tipo ( enum class Foo : uint8_t { ... }) con gcc 5.2.0 @ Linux e MinGW 4.9.3 @ Windows.
rr-

1
(... e in caso di std::numeric_limits<std::underlying_type<Foo>::type>::max(), restituisce il valore massimo del tipo sottostante cioè 0xFFFFFFFF per interi a 32 bit, che non è utile in questo contesto.)
rr-

1
Strano, perché ho un codice funzionante che fa quello che ho descritto. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Quindi: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) e std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;stampa il risultato atteso. Testato con gusti gcc 5.2.1 e 4.8 / 4.9.
Wojciech Migda

2
-1; ping me se cambi la risposta, così posso annullare il downvote. Questa risposta è una pessima convenzione da seguire. È un uso improprio del concetto di numeric_limits<T>::max(). L'unica cosa che la funzione potrebbe ragionevolmente restituire è il valore enumerato più alto. Tornerebbe 2, ma l'OP (in questo caso specifico) avrebbe bisogno che tornasse 3. Una volta che hai valori non predefiniti per enum ( FB = 2057), tutte le scommesse sono disattivate, non puoi nemmeno + 1aggirare l'errore off-by-one. Se ci fosse un numeric_limits<T>::number_of_elements_of_the_set()(o un nome più breve), potrebbe essere usato senza ambiguità.
Merlyn Morgan-Graham

3

Aggiungi una voce, alla fine della tua enumerazione, chiamata Folders_MAX o qualcosa di simile e usa questo valore quando inizializzi i tuoi array.

ContainerClass* m_containers[Folders_MAX];

2

Mi piace usare le enumerazioni come argomenti per le mie funzioni. È un mezzo semplice per fornire un elenco fisso di "opzioni". Il problema con la risposta più votata qui è che usando quella, un cliente può specificare una "opzione non valida". Come spin off, consiglio di fare essenzialmente la stessa cosa, ma usa una costante int al di fuori dell'enum per definirne il conteggio.

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

Non è piacevole, ma è una soluzione pulita se non si modifica l'enumerazione senza aggiornare la costante.


1

Non vedo davvero alcun modo per ottenere davvero il numero di valori in un'enumerazione in C ++. Qualsiasi soluzione prima menzionata funziona fintanto che non definisci il valore delle tue enumerazioni se definisci un valore che potresti imbatterti in situazioni in cui crei array troppo grandi o troppo piccoli

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

in questo esempio di esempi il risultato creerebbe un array di 3 elementi quando è necessario un array di 5 elementi

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

in questo esempio di esempi il risultato creerebbe un array di 301 elementi quando è necessario un array di 5 elementi

Il modo migliore per risolvere questo problema nel caso generale sarebbe quello di iterare attraverso le tue enumerazioni ma questo non è ancora nello standard per quanto ne so

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.