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 ++?


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.


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

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 ....


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)


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

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

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.


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)

}

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.


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.


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, ...
    // ...
}

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

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

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.


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.


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.


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

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.


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.


-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.

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.