(Jak) mogę policzyć elementy w wyliczeniu?


98

To pytanie przyszło mi do głowy, gdy miałem coś takiego

enum Folders {FA, FB, FC};

i chciałem utworzyć tablicę kontenerów dla każdego folderu:

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

(Korzystanie z map jest o wiele bardziej eleganckie w użyciu std::map<Folders, ContainerClass*> m_containers;:)

Ale wracając do mojego pierwotnego pytania: co jeśli nie chcę na stałe zakodować rozmiaru tablicy, czy jest sposób, aby dowiedzieć się, ile elementów znajduje się w folderach? (Bez polegania np. Na FCtym, że jest ostatnią pozycją na liście, co pozwoliłoby na coś takiego, ContainerClass*m_containers[FC+1]jeśli się nie mylę).


Ten post może odpowiedzieć na Twoje pytanie: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked

1
Pytanie jest trochę niedookreślone. Zgodnie ze standardem C ++ int(FA) | int(FB) | int (FC)jest również wartością prawną dla Folderszmiennej. Jeśli ustalasz rozmiar m_containerstak, aby każda Folderszmienna była prawidłowym indeksem, [FC+1]nie będzie wystarczająco duża.
MSalters

Zapytałem o bardzo
podobną

Polecam Ci rozwiązanie ze stackoverflow.com/a/60216003/12894563 i ulepszony wariant ze stackoverflow.com/a/60271408/12894563
ixjxk

Odpowiedzi:


123

Nie ma na to dobrego sposobu, zwykle w wyliczeniu widzisz dodatkową pozycję, tj

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

Więc możesz zrobić:

int fuz[FOOBAR_NR_ITEMS];

Wciąż niezbyt miło.

Ale oczywiście zdajesz sobie sprawę, że sama liczba elementów w wyliczeniu nie jest bezpieczna, biorąc pod uwagę np

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

liczba elementów wyniosłaby 4, ale wartości całkowite wartości wyliczenia byłyby poza zakresem indeksu tablicy. Używanie wartości wyliczeniowych do indeksowania tablic nie jest bezpieczne, należy rozważyć inne opcje.

edycja: zgodnie z żądaniem sprawiła, że ​​specjalny wpis bardziej się wyróżniał.


27
Nazwij to LAST lub ALWAYS_AT_END lub coś nie tak tajemniczego. Spraw, by wystawał . Aby kolejni opiekunowie nie dodawali przypadkowo nowych wpisów po zakończeniu znacznika.
Martin York

3
Na dobre lub na złe takie podejście stosujemy w naszej organizacji. Zwykle nazywamy to FINAL_enumname_ENTRY, na przykład FINAL_foobar_ENTRY. Widziałem również ludzi używających oddzielnej zmiennej statycznej const FOOBAR_COUNT zdefiniowanej zaraz po deklaracji wyliczenia, podejście, które jest nieco bardziej podatne na błędy.
Darryl,

1
Przynajmniej dość łatwo zauważyć, że „enum foo {a = 10, LAST}” będzie dziwne. I pomyślałem, że „int arr [LAST]” będzie w tym przypadku 11 pozycji, a nie 2, więc większość kodu będzie działać (ale marnujesz pamięć na nieprawidłowe wartości indeksu)
Code Abominator

32

W przypadku języka C ++ dostępne są różne bezpieczne dla typu techniki wyliczenia , a niektóre z nich (takie jak proponowany, ale nigdy nie przesłany Boost.Enum ) obejmują obsługę pobierania rozmiaru wyliczenia.

Najprostszym podejściem, które działa zarówno w C, jak i C ++, jest przyjęcie konwencji deklarowania wartości ... MAX dla każdego z typów wyliczeń:

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

Edit : Odnośnie { FA, FB, FC, Folders_MAX = FC}kontra {FA, FB, FC, Folders_MAX]: Wolę ustawienie wartości MAX ... do ostatniej wartości prawnej wyliczenia z kilku powodów:

  1. Nazwa stałej jest technicznie dokładniejsza (ponieważ Folders_MAXpodaje maksymalną możliwą wartość wyliczenia).
  2. Osobiście czuję, że Folders_MAX = FCwyróżnia się nieco bardziej na tle innych wpisów (co utrudnia przypadkowe dodanie wartości wyliczenia bez aktualizowania wartości maksymalnej, problem, do którego odwoływał się Martin York).
  3. GCC zawiera przydatne ostrzeżenia, takie jak „wartość wyliczenia nieuwzględniona w przełączniku” dla kodu, takiego jak poniższy. Zezwalanie Folders_MAX == FC + 1 łamie te ostrzeżenia, ponieważ w rezultacie otrzymujesz kilka ... MAX wartości wyliczeniowych, które nigdy nie powinny być uwzględnione w przełączniku.
przełącznik (folder) 
{
  sprawa FA: ...;
  sprawa FB:…;
  // Ups, zapomniałem FC!
}

3
Dlaczego nie: enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];?
Bill

1
Wolę wyjaśnić, że ostatnia to liczba, a wszyscy mają to samo imię dzięki:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte

2
Właściwie czuję, że te „pomocne” ostrzeżenia to właściwy ból w dupie. Lubię dobre ostrzeżenia, które zawsze ustawiam -Ściany -pedantyczny itp., Kiedy się rozwijam, ale te ostrzeżenia są po prostu głupie. Jest tylko kilka gorszych, takich jak sugerowanie parens dla && || i & ^ | pierwszeństwo operatorów. To znaczy, myślałem, że java jest językiem opiekunki, co do cholery dzieje się z C i C ++ ...
co

2
Wadą posiadania Folders_max = FC jest to, że musisz to zmienić za każdym razem, gdy dodajesz coś do wyliczenia!
Étienne

2
enum Folders {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Żeby tylko podkreślić, że jest to przydatne w iteracji?
gjpc

7

A co z cechami w stylu STL? Na przykład:

enum Foo
{
    Bar,
    Baz
};

Napisać

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

specjalizacja (prawdopodobnie constexpr, jeśli używasz C ++ 11). Następnie w kodzie testowym wprowadź wszelkie statyczne potwierdzenia, aby zachować ograniczenia std :: numeric_limits :: max () = last_item.


2
Niestety to nie zadziała zgodnie z tą odpowiedzią .
rr-

3
std::numeric_limits<enum Foo>::max()zawsze zwraca zero ... (zobacz pytanie dla połączonej odpowiedzi) Testowane na normalnych wyliczeniach ( enum Foo { ... }), wyliczeniach z podpowiedziami ( enum class Foo : uint8_t { ... }) z gcc 5.2.0 @ Linux i MinGW 4.9.3 @ Windows.
rr-

1
(... iw przypadku std::numeric_limits<std::underlying_type<Foo>::type>::max(), zwraca maksymalną wartość typu bazowego, tj. 0xFFFFFFFF dla 32-bitowych liczb całkowitych, co nie jest przydatne w tym kontekście.)
rr-

1
Dziwne, bo mam działający kod, który robi to, co opisałem. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Następnie: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) I std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;wypisuje oczekiwany wynik. Testowane ze smakami gcc 5.2.1 i 4.8 / 4.9.
Wojciech Migda

2
-1; daj mi znać, jeśli zmienisz odpowiedź, żebym mógł cofnąć głos przeciw. Ta odpowiedź jest złą konwencją. Jest to nadużycie pojęcia numeric_limits<T>::max(). Jedyną rzeczą, którą funkcja może rozsądnie zwrócić, jest najwyższa wyliczona wartość. Powróciłby 2, ale PO (w tym konkretnym przypadku) potrzebowałby go do powrotu 3. Gdy już masz wartości inne niż domyślne dla enum ( FB = 2057), wszystkie zakłady są wyłączone, nie możesz nawet + 1zhakować błędu off-by-one. Gdyby istniała numeric_limits<T>::number_of_elements_of_the_set()(lub krótsza nazwa), można by jej użyć bez niejasności.
Merlyn Morgan-Graham

3

Dodaj wpis na końcu swojego wyliczenia o nazwie Folders_MAX lub podobny i użyj tej wartości podczas inicjowania tablic.

ContainerClass* m_containers[Folders_MAX];

2

Lubię używać wyliczeń jako argumentów w moich funkcjach. Zapewnienie stałej listy „opcji” jest łatwym sposobem. Problem z najczęściej głosowaną odpowiedzią polega na tym, że używając jej, klient może określić „niepoprawną opcję”. Jako spin-off, zalecam zrobienie zasadniczo tego samego, ale użycie stałej int poza wyliczeniem, aby zdefiniować ich liczbę.

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

To nie jest przyjemne, ale jest to czyste rozwiązanie, jeśli nie zmienisz wyliczenia bez aktualizacji stałej.


1

Naprawdę nie widzę sposobu, aby naprawdę uzyskać liczbę wartości w wyliczeniu w C ++. Każde z powyższych rozwiązań działa tak długo, jak długo nie zdefiniujesz wartości swoich wyliczeń, jeśli zdefiniujesz swoją wartość, że możesz napotkać sytuacje, w których utworzysz tablice za duże lub za małe

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

w tym o przykładach wynik utworzyłby tablicę 3 pozycji, gdy potrzebna jest tablica 5 pozycji

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

w tym o przykładach wynik utworzyłby tablicę 301 elementów, gdy potrzebna jest tablica 5 elementów

Najlepszym sposobem rozwiązania tego problemu w ogólnym przypadku byłoby powtórzenie wyliczeń, ale o ile wiem, nie jest to jeszcze standardowe

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.