Am observat că nu puteți utiliza standard de operatorii de matematica pe un enum astfel de ca ++ sau +=
Deci, ce este cel mai bun mod de a itera prin toate valorile într-un C++ enum?
Modul tipic este după cum urmează:
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
Vă rugăm să rețineți, enum "Ultima" este menit să fie sarit de repetare. Utilizând acest "fals" "Ultima" enum, nu't trebuie să actualizați de încheiere starea în buclă la ultima "reală" enum fiecare dată când doriți să adăugați un nou enum. Dacă doriți să adăugați mai multe enum mai târziu, doar adăugați-le înainte de Ultimul. Bucla în acest exemplu va funcționa în continuare.
Desigur, aceasta se descompune dacă enum valori sunt specificate:
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
Acest lucru ilustrează faptul că un enum nu este într-adevăr menit să repetăm. Modul tipic de a face cu un enum este să-l folosească într-un switch.
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
Dacă doriți cu adevărat pentru a enumera, chestii enum valori într-un vector și repeta peste asta. Acest lucru se va face în mod corespunzător cu cele specificate enum valori la fel de bine.
#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;
}
Dacă enum începe cu 0 și creștere este întotdeauna 1.
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
Dacă nu, cred că doar de ce este de a crea ceva ca un
vector<enumType> vEnums;
adăugați elemente, și de a folosi normal iteratori....
Cu c++11, acolo este de fapt o alternativă: să scrie un simplu templatized personalizate iterator.
las's presupunem enum este
enum class foo {
one,
two,
three
};
Acest cod generic va face truc, destul de eficient - loc într-un generic antet, l'll servi pentru orice enum ai putea avea nevoie pentru a repeta peste:
#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; }
};
Te'll nevoie să se specializeze se
typedef Iterator<foo, foo::one, foo::three> fooIterator;
Și apoi puteți repeta folosind gama-pentru
for (foo i : fooIterator() ) { //notice the parentheses!
do_stuff(i);
}
Presupunerea că tu nu't au lacune în enum este încă adevărat; nu există nici o presupunere privind numărul de biți de fapt nevoie pentru a stoca enum valoare (datorită std::underlying_type)
complicat prea mult aceste soluție, eu fac asa :
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};
const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };
for (NodePosition pos : NodePositionVector) {
...
}
Eu de multe ori fac asta
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))
{}
sau dacă nu succesive, dar cu pas regulat (de exemplu, pic steaguri)
enum EAnimal
{
E_First,
E_None = E_First,
E_CanFly = 0x1,
E_CanWalk = 0x2
E_CanSwim = 0x4,
E_Last
}
for (EAnimali = E_First; i < E_Last; i = EAnimal(i << 1))
{}
Ceva care s't a fost acoperit în alte răspunsuri = dacă're folosind puternic tastat C++11 enum, nu puteți utiliza ++
sau + int
pe ele. În acest caz, un pic mai dezordonat soluție este necesar:
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)
}
Puteți încerca și definiți următoarele macro:
#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))
Acum puteți folosi:
enum Count { zero, one, two, three };
for_range (Count, c, zero, three)
{
cout << "forward: " << c << endl;
}
Acesta poate fi folosit pentru a repeta înainte și înapoi prin nesemnate, numere întregi, enum și caractere:
for_range (unsigned, i, 10,0)
{
cout << "backwards i: " << i << endl;
}
for_range (char, c, 'z','a')
{
cout << c << endl;
}
În ciuda ciudat definiție este optimizat foarte bine. M-am uitat la disassembler in VC++. Codul este extrem de eficient. Don't fi puse în afara, dar pentru trei declarații: compilatorul va produce doar o buclă după optimizare! Puteți defini chiar închise bucle:
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;
}
Evident nu se poate repeta prin enumerate tipuri cu lacune.
Presupunând că enum este numerotat secvențial este predispus la erori. Mai mult decât atât, poate doriți să itera peste selectate recenzorii numai. Dacă acest subset este mic, looping peste ea în mod explicit, ar putea fi o alegere eleganta:
enum Item { Man, Wolf, Goat, Cabbage }; // or enum class
for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
// ...
}
Dacă nu vă place să polueze ai enum cu un număr articol (pentru că poate dacă utilizați, de asemenea, enum într-un comutator, atunci compilatorul va avertiza de un caz de dispariție CONTA:), puteți face acest lucru:
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);
}
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]
}
Pentru MS compilatoare:
#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);
}
Notă: aceasta este o mulțime de cod mai puțin decât simpla templatized personalizate iterator răspuns.
Puteți obține acest lucru cu GCC, prin utilizarea typeof "în loc de" decltype`, dar eu nu't au compilator la îndemână în momentul de față pentru a vă asigura că compilează.
Aici's o altă soluție care funcționează numai pentru învecinate enum. Acesta oferă așteptat iterație, cu excepția urâțenie în creștere, care este în cazul în care acesta face parte, din moment ce's ce's a rupt in 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))
{
// ...
}
``c++ enum class A { a0=0, a3=3, a4=4 }; constexpr std::array<a, 3> ALL_A {A::a0, O::a3, O::a4}; // constexpr este important aici
pentru(a o: ALL_A) {
if(a==O::a0 || o==O::a4) std::cout << static_cast
O constexpr std::array
poate repeta chiar non-secvențială a enum fără matrice a fi instanțiată de compilator. Acest lucru depinde de lucruri cum ar fi compilatorul's de optimizare euristica și dacă luați matrice's adresa.
Din experimentele mele, am constatat că g++
9.1 cu -O3
va optimiza departe de mai sus matrice dacă există 2 non-secvențială valori sau destul de câteva secvențială valori (am testat până la 6). Dar nu numai acest lucru, dacă aveți un "dacă" declarație. (Am încercat-o declarație pe care a comparat-o valoare întreagă mai mare decât toate elementele într-o matrice secventiala și inline repetare în ciuda niciunul fiind excluse, dar când am plecat la if, valorile au fost puse în memorie.) De asemenea, inline 5 valori de la un non-secvențială enum în [un caz|https://godbolt.org/z/XuGtoc]. Bănuiesc că acest comportament ciudat este cauza profundă euristici având de-a face cu cache-uri și sucursale de predicție.
Aici este un link-ul de la un simplu test de repetare pe godbolt care demonstrează matrice nu întotdeauna instanțiată.
Prețul acestei tehnici este scris enum elementele de două ori și păstrând cele două liste în sincronizare.
Dacă ai ști că enum valorile au fost secvențiale, de exemplu Qt:Cheie enum, ai putea:
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);
}
}
Acesta funcționează cum era de așteptat.
Doar asigurați-un tablou de int și bucla peste matrice, dar face ultimul element spun -1 și să-l utilizați pentru condiție de ieșire.
Dacă enum este:
enum MyEnumType{Hay=12,Grass=42,Beer=39};
apoi a crea matrice:
int Array[] = {Hay,Grass,Beer,-1};
for (int h = 0; Array[h] != -1; h++){
doStuff( (MyEnumType) Array[h] );
}
Acest lucru nu se rupe în jos, indiferent de int în reprezentarea atâta timp cât -1 verificare nu se ciocnesc cu unul dintre elementele desigur.