Comment puis-je itérer sur un enum?
je viens de remarquer que vous ne pouvez pas utiliser les opérateurs mathématiques standard sur un enum tel que ++ ou + =
alors quelle est la meilleure façon d'itérer toutes les valeurs dans un enum C++?
16 réponses
la voie typique est la suivante:
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
bien sûr, cela se décompose si les valeurs enum sont spécifiées:
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
Cela illustre qu'un enum n'est pas vraiment le but de parcourir. La façon typique de traiter un enum est de l'utiliser dans une instruction de commutation.
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
si vous voulez vraiment énumérer, remplissez les valeurs enum dans un vecteur et itérez-les. Cela permettra de traiter correctement l' les valeurs enum spécifiées aussi.
#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;
}
si votre enum commence par 0 et que l'incrément est toujours 1.
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
sinon je suppose que la seule raison est de créer quelque chose comme un
vector<enumType> vEnums;
ajouter les éléments et utiliser des itérateurs normaux....
avec c++11, Il y a en fait une alternative: écrire un simple itérateur personnalisé templatisé.
supposons que votre énum est
enum class foo {
one,
two,
three
};
ce code générique fera l'affaire, assez efficacement-place dans un en-tête générique, il vous servira pour tout enum que vous pourriez avoir besoin d'itérer sur:
#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; }
};
vous aurez besoin de le spécialiser
typedef Iterator<foo, foo::one, foo::three> fooIterator;
et ensuite vous pouvez itérer en utilisant la gamme-for
for (foo i : fooIterator() ) { //notice the parenteses!
do_stuff(i);
}
l'hypothèse que vous n'avez pas de lacunes dans votre enum est toujours vraie; il n'y a pas d'hypothèse sur le nombre de bits réellement nécessaires pour stocker la valeur d'enum (grâce à std:: underlying_type)
trop compliqué cette solution, je fais comme ça:
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};
const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };
for (NodePosition pos : NodePositionVector) {
...
}
vous ne pouvez pas avec un enum. Peut-être qu'un enum n'est pas le meilleur pour votre situation.
une convention courante est de nommer la dernière valeur enum quelque chose comme MAX et de l'utiliser pour contrôler une boucle en utilisant un int.
vous pouvez essayer de définir la macro suivante:
#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))
Maintenant vous pouvez l'utiliser:
enum Count { zero, one, two, three };
for_range (Count, c, zero, three)
{
cout << "forward: " << c << endl;
}
il peut être utilisé pour itérer en arrière et en avant par des entiers, des énums et des caractères non signés:
for_range (unsigned, i, 10,0)
{
cout << "backwards i: " << i << endl;
}
for_range (char, c, 'z','a')
{
cout << c << endl;
}
malgré sa définition maladroite il est très bien optimisé. J'ai regardé disassembler en VC++. Le code est extrêmement efficace. Ne soyez pas rebutés, mais les trois états: le compilateur produira une seule boucle après l'optimisation! Vous pouvez même définir des boucles fermées:
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;
}
vous ne pouvez évidemment pas itérer à travers les types énumérés avec des lacunes.
quelque chose qui n'a pas été couvert dans les autres réponses = si vous utilisez des enums C++11 fortement dactylographiés, vous ne pouvez pas utiliser ++
ou + int
sur eux. Dans ce cas, un peu de solution de messier est nécessaire:
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)
}
vous pouvez également surcharger les opérateurs d'incrément/décrément pour votre type énuméré.
si vous n'aimez pas polluer l'enum avec un élément de comptage final (parce que peut-être si vous utilisez aussi l'enum dans un commutateur alors le compilateur vous avertira d'un nombre de cas manquant:), vous pouvez faire ceci:
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);
}
pour les compilateurs 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);
}
Note: c'est beaucoup moins de code que la simple réponse itératrice personnalisée.
vous pouvez obtenir cela pour travailler avec GCC en utilisant typeof
au lieu de decltype
, mais je n'ai pas ce compilateur à portée de main pour le moment pour s'assurer qu'il compile.
je fais souvent ça comme ça
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))
{}
ou, si elle n'est pas successive, mais avec un pas régulier (p.ex. des indicateurs de bits)
enum EMyEnum
{
E_First,
E_None = E_First,
E_Green = 0x1,
E_White = 0x2
E_Blue = 0x4,
E_Last
}
for (EMyEnum i = E_First; i < E_Last; i = EMyEnum(i << 1))
{}
L'une des réponses dit:"Si vous saviez que les valeurs enum étaient séquentielles, par exemple L'enum Qt:Key".
Qt:: les valeurs clés ne sont pas séquentielles . Certains segments de l'enum.
ce fil est sur itération sur toutes les valeurs en enum. Cela est en fait possible dans Qt en raison de son utilisation de Meta Object System:
const QMetaObject *metaObject = qt_getQtMetaObject();
QMetaEnum keyEnum = metaObject->enumerator(metaObject->indexOfEnumerator("Key"));
for (int i = 0; i < keyEnum.keyCount(); ++i) {
qDebug() << keyEnum.key(i);
}
Voir aussi QObject::metaObject() et q_enum macro.
je pense que ce genre de choses va devenir plus facile avec C++20? Mais je n'ai pas regardé.
C++ n'a pas d'introspection, donc vous ne pouvez pas déterminer ce genre de chose à l'exécution.
si vous saviez que les valeurs enum étaient séquentielles, par exemple L'enum Qt: Key, vous pourriez:
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);
}
}
ça marche comme prévu.
il suffit de faire un tableau des ints et boucle sur le tableau, mais faites le dernier élément dire -1 et utilisez-le pour la condition de sortie.
si enum est:
enum MyEnumType{Hay=12,Grass=42,Beer=39};
crée alors array:
int Array[] = {Hay,Grass,Beer,-1};
for (int h = 0; Array[h] != -1; h++){
doStuff( (MyEnumType) Array[h] );
}
cela ne se décompose pas quelle que soit l'entrée dans la représentation aussi longtemps que -1 check ne se heurte pas à l'un des éléments bien sûr.