Jak mogę iterować po wyliczeniu?

303

Właśnie zauważyłem, że nie można używać standardowych operatorów matematycznych na wyliczeniu, takich jak ++ lub + =

Więc jaki jest najlepszy sposób na iterację wszystkich wartości w wyliczeniu C ++?

Adam
źródło
2
Jedno z wielu podejść: When enum Just Is not Enough: Klasy wyliczeń dla C ++ . A jeśli chcesz czegoś bardziej enkapsulowanego, wypróbuj to podejście Jamesa Kanze.
Don Wakefield,
Połączone elementy mają kilka interesujących odpowiedzi.
Tony
Te odpowiedzi wydają się nie obejmować problemu, który intmoże nie być wystarczająco duży! ( [C++03: 7.2/5])
Wyścigi lekkości na orbicie
Co ciekawe, możesz zdefiniować operator++wyliczenia; jednak możesz to zrobić for(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++). Pamiętaj, że musisz przesyłać 0do, Enum_Eponieważ C ++ zabrania operatorom przypisywania wyliczeń.
weberc2
Gdyby istniał operator czasu kompilacji, podobny do sposobu działania sizeof, który mógłby emitować literał std :: initializer_list składający się z wartości wyliczenia, mielibyśmy rozwiązanie i nie wymagalibyśmy narzutu w czasie wykonywania.
jbruni

Odpowiedzi:

263

Typowy sposób jest następujący:

enum Foo {
  One,
  Two,
  Three,
  Last
};

for ( int fooInt = One; fooInt != Last; fooInt++ )
{
   Foo foo = static_cast<Foo>(fooInt);
   // ...
}

Uwaga: wyliczanie Lastma być pomijane przez iterację. Korzystając z tego „fałszywego” Lastwyliczenia, nie musisz aktualizować warunku zakończenia w pętli for do ostatniego „prawdziwego” wyliczenia za każdym razem, gdy chcesz dodać nowy wyliczenie. Jeśli chcesz dodać więcej wyliczeń później, po prostu dodaj je przed Ostatnim. Pętla w tym przykładzie nadal będzie działać.

Oczywiście to się psuje, jeśli określono wartości wyliczeniowe:

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

To pokazuje, że wyliczenie nie jest tak naprawdę przeznaczone do iteracji. Typowym sposobem radzenia sobie z wyliczeniem jest użycie go w instrukcji switch.

switch ( foo )
{
    case One:
        // ..
        break;
    case Two:  // intentional fall-through
    case Three:
        // ..
        break;
    case Four:
        // ..
        break;
     default:
        assert( ! "Invalid Foo enum value" );
        break;
}

Jeśli naprawdę chcesz wyliczyć, umieść wartości wyliczenia w wektorze i powtórz to. To również odpowiednio poradzi sobie z określonymi wartościami wyliczania.

andreas buykx
źródło
13
Zauważ, że w pierwszej części przykładu, jeśli chcesz użyć „i” jako wyliczenia Foo, a nie jako int, musisz rzucić to w następujący sposób: static_cast <Foo> (i)
Clayton
5
Pomijasz również ostatnią pętlę. Powinno być <= ostatnie
Tony
20
@ Tony Last ma zostać pominięty. Jeśli chcesz dodać więcej wyliczeń później, dodaj je przed Last ... Pętla w pierwszym przykładzie nadal będzie działać. Używając „fałszywego” ostatniego wyliczenia, nie musisz aktualizować warunku zakończenia w pętli for do ostatniego „rzeczywistego” wyliczenia za każdym razem, gdy chcesz dodać nowy wyliczenie.
timidpueo
Z wyjątkiem tego, że faktycznie przydzielono pamięć, gdy wyliczenie, pod warunkiem, że ma indeks zerowy i jest ściśle ciągłe, może wykonać to zadanie bez przydzielania pamięci.
Chmura
1
Należy pamiętać, że aby ta definicja wyliczenia była bezpieczna dla aktualizacji, należy zdefiniować wartość UNKNOWN = 0. Ponadto sugerowałbym po prostu porzucenie defaultskrzynki przy przełączaniu wartości wyliczeniowych, ponieważ może to ukryć przypadki, w których obsługa wartości została zapomniana do czasu wykonania. Zamiast tego należy na stałe zakodować wszystkie wartości i użyć UNKNOWNpola do wykrycia niezgodności.
Benjamin Bannier
53
#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;
}
zdf
źródło
Dzięki! Zauważ, że jeśli krzyżujesz pliki / klasy i jeśli kompatybilność z MS powoduje problemy z zadeklarowanymi w nagłówku stałymi liczbami całkowitymi, pomaga mojemu kompilatorowi jawnie umieścić rozmiar w typie w nagłówku: static const Type All[3];i wtedy jestem w stanie aby zainicjować w źródle: const MyEnum::Type MyEnum::All[3] = { a, b, c }; Zanim to zrobiłem, otrzymywałem nieprzyjemne Error in range-based for...błędy (ponieważ tablica miała nieznany rozmiar). Zrozumiałem to dzięki pokrewnej odpowiedzi
mędrzec
1
Wersja tablicy jest bardzo przyjazna dla kopiowania wklej. Najbardziej zadowalająca odpowiedź poza „NIE” lub „tylko dla sekwencji”. Prawdopodobnie nawet przyjazny dla makr.
Paulo Neves,
1
może to być dobre rozwiązanie w przypadku wyliczeń z małą liczbą przedmiotów, ale w przypadku wyliczeń z dużą liczbą przedmiotów nie musi dobrze pasować.
kato2
20

Jeśli twoje wyliczenie zaczyna się od 0, a przyrost wynosi zawsze 1.

enum enumType 
{ 
    A = 0,
    B,
    C,
    enumTypeEnd
};

for(int i=0; i<enumTypeEnd; i++)
{
   enumType eCurrent = (enumType) i;            
}

Jeśli nie, myślę, że jedynym powodem jest stworzenie czegoś takiego

vector<enumType> vEnums;

dodaj elementy i użyj zwykłych iteratorów ....

João Augusto
źródło
19

W wersji c ++ 11 istnieje alternatywa: napisanie prostego niestandardowego iteratora opartego na szablonie.

załóżmy, że twój enum jest

enum class foo {
  one,
  two,
  three
};

Ten ogólny kod wykona tę sztuczkę, dość skutecznie - umieść w ogólnym nagłówku, będzie ci służył do każdego wyliczenia, które może być konieczne do iteracji:

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

Musisz go specjalizować

typedef Iterator<foo, foo::one, foo::three> fooIterator;

A potem możesz iterować za pomocą zasięgu

for (foo i : fooIterator() ) { //notice the parentheses!
   do_stuff(i);
}

Założenie, że nie masz luk w swoim wyliczeniu, jest nadal prawdziwe; nie ma założenia co do liczby bitów faktycznie potrzebnych do przechowywania wartości wyliczeniowej (dzięki std :: bazowy_typ)

Francesco Chemolli
źródło
1
@lepe? Po prostu tworzysz inny typefef dla innego wyliczenia.
Andrew Lazarus
2
@lepe To tak, jakby powiedzieć, że std::vectorto nie jest ogólne, ponieważ std::vector<foo>jest powiązane foo.
Kyle Strand,
1
typedef Iterator<color, color::green, color::red> colorIterator;Upewnij się, że rozumiesz, jak działają instancje szablonów.
Andrew Lazarus,
2
Och, widzę problem - foo operator*() { ...powinien być C operator*() { ....
Kyle Strand,
1
@KyleStrand: Masz to! to ma teraz całkowicie sens. Czy kod powinien zostać zaktualizowany? Dziękuję wszystkim za wyjaśnienia.
lepe
16

zbyt skomplikowane te rozwiązania, lubię to:

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

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

for (NodePosition pos : NodePositionVector) {
...
}
Enzojz
źródło
Nie wiem, dlaczego to zostało odrzucone. To rozsądne rozwiązanie.
Paul Brannan,
11
Spodziewam się, że było tak, ponieważ wpisy muszą być przechowywane w dwóch miejscach.
Ant
Czy C ++ pozwala na for (NodePosition pos : NodePositionVector)składnię? O ile mi wiadomo, jest to składnia Java i będziesz potrzebować iteratorów w C ++, aby zrobić coś równoważnego.
thegreatjedi
3
@thegreatjedi Od C ++ 11 możesz, nawet łatwiej: for (auto pos: NodePositionVector) {..}
Enzojz
@thegreatjedi Szybciej byłoby wyszukać, a nawet skompilować program testowy, niż zadać to pytanie. Ale tak, ponieważ od C ++ 11 jest to całkowicie poprawna składnia C ++, którą kompilator tłumaczy na równoważny (i dużo bardziej szczegółowy / mniej abstrakcyjny) kod, zwykle przez iteratory; patrz cppreference . I, jak powiedział Enzojz, C ++ 11 również dodał auto, więc nie musisz jawnie deklarować rodzaju elementów, chyba że (A) musisz użyć operatora konwersji lub (B) autoz jakiegoś powodu nie lubisz . Większość forużytkowników zakresu korzysta z autoAFAICT
podkreślenie_d
9

Często tak robię

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

lub jeśli nie sukcesywnie, ale z regularnym krokiem (np. flagi bitowe)

    enum EAnimalCaps
    {
        E_First,
        E_None    = E_First,
        E_CanFly  = 0x1,
        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))
    {}
Niki
źródło
ale po co drukować wartości bitowo?
Anu
1
Aby użyć wyliczeń, aby utworzyć maski bitowe. np. połącz kilka opcji w jedną zmienną, a następnie użyj FOR do przetestowania każdej opcji. Naprawiono mój post z lepszym przykładem.
Niki
Nadal nie mogę z niego skorzystać (a Twój post wciąż pokazuje stary przykład)! Używanie enum jako masek bitowych jest naprawdę pomocne, ale nie było w stanie połączyć kropek! czy mógłbyś szczegółowo rozwinąć swój przykład w swoim przykładzie, możesz również wstawić dodatkowy kod.
Anu
@anu Przepraszamy, nie widziałem Twojego komentarza. Dodano klasę Żaby jako przykład maski bitowej
Niki
8

Nie możesz wyliczyć. Może wyliczenie nie najlepiej pasuje do twojej sytuacji.

Powszechną konwencją jest nazywanie ostatniej wartości wyliczenia czegoś takiego jak MAX i używanie jej do sterowania pętlą za pomocą int.

Corey Trager
źródło
Istnieje kilka przykładów, które pokazują coś przeciwnego. W swoim własnym oświadczeniu zaprzeczasz sobie (druga linia).
Niki,
6

Coś, co nie zostało uwzględnione w innych odpowiedziach = jeśli używasz silnie napisanych wyliczeń C ++ 11, nie możesz ich używać ++ani + intna nich. W takim przypadku wymagane jest trochę bałaganu:

enum class myenumtype {
  MYENUM_FIRST,
  MYENUM_OTHER,
  MYENUM_LAST
}

for(myenumtype myenum = myenumtype::MYENUM_FIRST;
    myenum != myenumtype::MYENUM_LAST;
    myenum = static_cast<myenumtype>(static_cast<int>(myenum) + 1)) {

  do_whatever(myenum)

}
Zamieszki
źródło
3
... ale C ++ 11 wprowadza oparty na tym zakresie zakres pokazany w innych odpowiedziach. :-)
mędrzec
5

Możesz spróbować zdefiniować następujące makro:

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

Teraz możesz go użyć:

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

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

Można go używać do iteracji w przód i w tył przez bez znaku, liczby całkowite, wyliczenia i znaki:

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


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

Pomimo niezręcznej definicji jest bardzo dobrze zoptymalizowany. Patrzyłem na deasembler w VC ++. Kod jest niezwykle wydajny. Nie zniechęcaj się, ale trzy instrukcje: kompilator wygeneruje tylko jedną pętlę po optymalizacji! Możesz nawet zdefiniować zamknięte pętle:

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

Oczywiście nie można iterować po wyliczonych typach z lukami.

Michaił Semenow
źródło
1
To wspaniały hack! Można powiedzieć, że chociaż jest bardziej odpowiedni dla C niż dla C ++.
einpoklum
3
_A1nie jest dozwoloną nazwą, jest wiodącym podkreśleniem z następującą wielką literą.
Martin Ueding,
3

Można także przeciążać operatory inkrementacji / dekrementacji dla wyliczonego typu.

JohnMcG
źródło
1
Nie można przeciążać żadnego operatora na typach wyliczonych w języku C lub C ++. Chyba że masz stworzyć strukturę / klasę, która emuluje wyliczenie wartości.
Trevor Hickey,
2
C ++ pozwala przeciążać operatorów na wyliczeniach. Zobacz stackoverflow.com/questions/2571456/… .
Arch D. Robison
Przeciążenie zwiększyć / zmniejszyć wymaga podejmowania decyzji o tym, co zrobić, gdy występuje przepełnienie
tytułowej
3

Zakładanie, że wyliczenie jest kolejno numerowane, jest podatne na błędy. Ponadto możesz iterować tylko nad wybranymi modułami wyliczającymi. Jeśli ten podzbiór jest mały, wyraźne zapętlenie może być eleganckim wyborem:

enum Item { Man, Wolf, Goat, Cabbage }; // or enum class

for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
    // ...
}
marski
źródło
Myślę, że to fajna opcja. Musi być częścią nowszej specyfikacji C ++, której używałem, kiedy zadawałem pytanie?
Adam
Tak. Iteruje po std :: initializer_list <Item>. link .
marski
2

Jeśli nie chcesz zanieczyszczać swojego wyliczenia końcowym elementem COUNT (ponieważ być może, jeśli użyjesz również wyliczenia w przełączniku, wtedy kompilator ostrzeże Cię o brakującej liczbie przypadków COUNT :), możesz to zrobić:

enum Colour {Red, Green, Blue};
const Colour LastColour = Blue;

Colour co(0);
while (true) {
  // do stuff with co
  // ...
  if (co == LastColour) break;
  co = Colour(co+1);
}
Niels Holst
źródło
2

Oto inne rozwiązanie, które działa tylko dla ciągłych wyliczeń. Daje oczekiwaną iterację, z wyjątkiem brzydoty w przyroście, do której należy, ponieważ to właśnie jest zepsute w C ++.

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

for (Bar foo = One; foo < End_Bar; foo = Bar(foo + 1))
{
    // ...
}
Ethan Bradford
źródło
1
Przyrost można skrócić do foo = Bar(foo + 1).
HolyBlackCat
Dzięki, HolyBlackCat, dołączyłem twoją doskonałą sugestię! Zauważam również, że Riot ma właściwie to samo rozwiązanie, ale zgodne z mocnym pisaniem (a więc bardziej gadatliwym).
Ethan Bradford,
2
enum class A {
    a0=0, a3=3, a4=4
};
constexpr std::array<A, 3> ALL_A {A::a0, A::a3, A::a4}; // constexpr is important here

for(A a: ALL_A) {
  if(a==A::a0 || a==A::a4) std::cout << static_cast<int>(a);
}

A constexpr std::arraymoże iterować nawet niesekwencyjne wyliczenia bez tworzenia instancji tablicy przez kompilator. Zależy to od heurystyki optymalizacyjnej kompilatora i od tego, czy weźmiesz adres tablicy.

W moich eksperymentach odkryłem, że g++9.1 z -O3zoptymalizuje powyższą tablicę, jeśli są 2 wartości niesekwencyjne lub całkiem kilka wartości sekwencyjnych (testowałem do 6). Ale robi to tylko wtedy, gdy masz ifoświadczenie. (Próbowałem instrukcji, która porównała liczbę całkowitą większą niż wszystkie elementy w tablicy sekwencyjnej i podniosła iterację, mimo że żadna nie została wykluczona, ale kiedy pominąłem instrukcję if, wartości zostały zapisane w pamięci.) wartości z niesekwencyjnego wyliczenia w [jednym przypadku | https://godbolt.org/z/XuGtoc] . Podejrzewam, że to dziwne zachowanie wynika z głębokiej heurystyki związanej z pamięcią podręczną i prognozowaniem gałęzi.

Oto link do prostej iteracji testu na godbolt, która pokazuje, że tablica nie zawsze jest tworzona .

Cena tej techniki polega na dwukrotnym zapisaniu elementów wyliczeniowych i zsynchronizowaniu dwóch list.

Tytułowy
źródło
Lubię prostą semantykę for-loop podobną do zakresu i myślę, że rozwinie się jeszcze bardziej, dlatego podoba mi się to rozwiązanie.
rtischer8277
2

W książce języka programowania C ++ Bjarne'a Stroustrupa możesz przeczytać, że proponuje on przeciążenie operator++twojego specyficznego enum. enumsą typami zdefiniowanymi przez użytkownika, a operator przeciążenia istnieje w języku dla tych konkretnych sytuacji.

Możesz kodować następujące elementy:

#include <iostream>
enum class Colors{red, green, blue};
Colors& operator++(Colors &c, int)
{
     switch(c)
     {
           case Colors::red:
               return c=Colors::green;
           case Colors::green:
               return c=Colors::blue;
           case Colors::blue:
               return c=Colors::red; // managing overflow
           default:
               throw std::exception(); // or do anything else to manage the error...
     }
}

int main()
{
    Colors c = Colors::red;
    // casting in int just for convenience of output. 
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    return 0;
}

kod testowy: http://cpp.sh/357gb

Pamiętaj, że używam enum class. Kod działa enumrównież dobrze z . Ale wolę, enum classponieważ są dobrze napisane i mogą zapobiec popełnieniu błędu w czasie kompilacji.

LAL
źródło
Głos ten został oddany. Jest jakiś powód, dla którego nie odpowiedziałby na pytanie?
LAL,
Powodem jest prawdopodobnie to, że jest to okropne rozwiązanie architektoniczne: zmusza cię do napisania logiki oznaczonej globalnie - do powiązanego z konkretnym komponentem (twoim wyliczeniem), ponadto, jeśli twoje wyliczenie zmienia się z jakiegokolwiek powodu, który zmuszony jest edytować + + operator również, ponieważ podejście to nie jest zrównoważone dla żadnego projektu na dużą skalę, nie jest zaskoczeniem, że pochodzi z rekomendacji Bjarne Stroustrup, w czasach, gdy architektura oprogramowania była jak science fiction
Manjia
Pierwotne pytanie dotyczy tego, czy operator ma telefon enum. To nie było pytanie architektoniczne. Nie wierzę, że w 2013 roku C ++ było science fiction.
LAL
1

W przypadku kompilatorów MS:

#define inc_enum(i) ((decltype(i)) ((int)i + 1))

enum enumtype { one, two, three, count};
for(enumtype i = one; i < count; i = inc_enum(i))
{ 
    dostuff(i); 
}

Uwaga: jest to o wiele mniej kodu niż prosta niestandardowa odpowiedź iteratora z szablonu.

Możesz sprawić, aby działał z GCC, używając typeofzamiast niego decltype, ale w tej chwili nie mam tego kompilatora, aby się upewnić, że się skompiluje.

użytkownik2407277
źródło
Zostało to napisane ~ 5 lat po tym, jak decltypestało się standardowym C ++, więc nie powinieneś polecać przestarzałej typeofstarożytnej GCC. Całkiem niedawno GCC radzi decltypesobie dobrze. Są inne problemy: odradzanie w stylu C jest odradzane, a makra gorsze. Właściwe funkcje C ++ mogą dać tę samą ogólną funkcjonalność. To byłoby lepsze przepisany w użyciu static_casti funkcję szablonu: template <typename T> auto inc_enum(T const t) { return static_cast<T>(static cast<int>(t) + 1); }. Odlewy nie są potrzebne w przypadku innych niż enum class. Alternatywnie, operatory mogą być przeciążone według enumtypu (TIL)
underscore_d
1
typedef enum{
    first = 2,
    second = 6,
    third = 17
}MyEnum;

static const int enumItems[] = {
    first,
    second,
    third
}

static const int EnumLength = sizeof(enumItems) / sizeof(int);

for(int i = 0; i < EnumLength; i++){
    //Do something with enumItems[i]
}
Justin Moloney
źródło
To rozwiązanie stworzy niepotrzebnie zmienne statyczne w pamięci, podczas gdy celem wyliczenia jest po prostu utworzenie „maski” dla
wbudowanych
Chyba że zmieniono naconstexpr static const int enumItems[]
Mohammad Kanan
0

C ++ nie ma introspekcji, więc nie można tego określić w czasie wykonywania.

kͩeͣmͮpͥ ͩ
źródło
1
Czy mógłbyś mi wyjaśnić, dlaczego „introspekcja” byłaby potrzebna do iteracji nad wyliczeniem?
Jonathan Mee
Może terminem jest Refleksja ?
kͩeͣmͮpͥ ͩ
2
Próbuję powiedzieć 2 rzeczy: 1) Na wiele innych odpowiedzi C ++ może to osiągnąć, więc jeśli masz zamiar powiedzieć, że nie może, wymagany jest link lub dalsze wyjaśnienia. 2) W obecnej formie jest to w najlepszym razie komentarz, a na pewno nie odpowiedź.
Jonathan Mee,
Głosuj za moją odpowiedzią - myślę, że bardziej niż uzasadniłeś
kͩeͩmͮpͥ ͩ
1
Znowu napiszę 2 komentarze: 1) Nie głosuję za odrzuceniem, ponieważ uważam, że otrzymanie głosowania negatywnie wpływa na uczestnictwo w stronie, uważam, że przynosi efekt przeciwny do zamierzonego 2) Nadal nie rozumiem, co próbujesz powiedzieć, ale brzmi jak rozumiesz coś, czego nie rozumiem, w którym to przypadku wolałbym, abyś opracował bardziej szczegółową odpowiedź niż usunął odrzuconą odpowiedź.
Jonathan Mee,
0

Jeśli wiesz, że wartości wyliczenia są sekwencyjne, na przykład wyliczanie Qt: Key, możesz:

Qt::Key shortcut_key = Qt::Key_0;
for (int idx = 0; etc...) {
    ....
    if (shortcut_key <= Qt::Key_9) {
        fileMenu->addAction("abc", this, SLOT(onNewTab()),
                            QKeySequence(Qt::CTRL + shortcut_key));
        shortcut_key = (Qt::Key) (shortcut_key + 1);
    }
}

Działa zgodnie z oczekiwaniami.

kcrossen
źródło
-1

Po prostu utwórz tablicę liczb całkowitych i zapętl ją nad tablicą, ale spraw, aby ostatni element powiedział -1 i użyj go jako warunku wyjścia.

Jeśli wyliczeniem jest:

enum MyEnumType{Hay=12,Grass=42,Beer=39};

następnie utwórz tablicę:

int Array[] = {Hay,Grass,Beer,-1};

for (int h = 0; Array[h] != -1; h++){
  doStuff( (MyEnumType) Array[h] );
}

Nie rozkłada się to bez względu na ints w reprezentacji, o ile kontrola -1 nie koliduje z jednym z elementów.

matematyk
źródło