16 ответов:
типичный способ заключается в следующем:
enum Foo { One, Two, Three, Last }; for ( int fooInt = One; fooInt != Last; fooInt++ ) { Foo foo = static_cast<Foo>(fooInt); // ... }конечно, это ломается, если указаны значения перечисления:
enum Foo { One = 1, Two = 9, Three = 4, Last };это иллюстрирует, что перечисление на самом деле не предназначено для итерации. Типичный способ работы с перечислением-использовать его в операторе switch.
switch ( foo ) { case One: // .. break; case Two: // intentional fall-through case Three: // .. break; case Four: // .. break; default: assert( ! "Invalid Foo enum value" ); break; }Если вы действительно хотите перечислить, введите значения перечисления в вектор и повторите это. Это также будет правильно работать с указанными значениями перечисления.
#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; }
если перечисление начинается с 0 и приращение всегда равно 1.
enum enumType { A = 0, B, C, enumTypeEnd }; for(int i=0; i<enumTypeEnd; i++) { enumType eCurrent = (enumType) i; }Если нет, я думаю, что единственное, почему это создать что-то вроде
vector<enumType> vEnums;добавьте элементы и используйте обычные итераторы....
С c++11 на самом деле есть альтернатива: написание простого шаблонного пользовательского итератора.
Предположим ваш enum-это
enum class foo { one, two, three };этот общий код будет делать трюк, довольно эффективно-поместите в общий заголовок, он будет служить вам для любого перечисления, которое вам может потребоваться повторить:
#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; } };вам нужно будет специализировать его
typedef Iterator<foo, foo::one, foo::three> fooIterator;и затем вы можете повторить с помощью range-for
for (foo i : fooIterator() ) { //notice the parenteses! do_stuff(i); }предположение, что вы этого не делаете иметь пробелы в вашем перечислении по-прежнему верно; нет предположения о количестве бит, фактически необходимых для хранения значения перечисления (благодаря std::underlying_type)
слишком сложно эти решения, мне это нравится:
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3}; const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary }; for (NodePosition pos : NodePositionVector) { ... }
вы не можете с помощью enum. Может быть, перечисление не лучше подходит для вашей ситуации.
общее соглашение заключается в том, чтобы назвать последнее значение перечисления чем-то вроде MAX и использовать его для управления циклом с помощью int.
вы можете попробовать и определить следующий макрос:
#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))теперь вы можете использовать его:
enum Count { zero, one, two, three }; for_range (Count, c, zero, three) { cout << "forward: " << c << endl; }Он может быть использован для итерации назад и вперед через беззнаковые, целые числа, перечисления и символы:
for_range (unsigned, i, 10,0) { cout << "backwards i: " << i << endl; } for_range (char, c, 'z','a') { cout << c << endl; }несмотря на его неудобное определение он оптимизирован очень хорошо. Я посмотрел на дизассемблер в VC++. Код является чрезвычайно эффективным. Не откладывайте, но три для операторов: компилятор будет производить только один цикл после оптимизации! Вы даже можете определите замкнутые циклы:
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; }вы, очевидно, не можете перебирать перечисленные типы с пробелами.
что-то, что не было охвачено в других ответах = если вы используете строго типизированные перечисления C++11, вы не можете использовать
++или+ intна них. В этом случае требуется немного более грязное решение: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) }
Если вам не нравится загрязнять вас перечислением с конечным элементом подсчета (потому что, возможно, если вы также используете перечисление в коммутаторе, то компилятор предупредит вас о пропущенном количестве случаев:), вы можете сделать это:
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); }
для компиляторов 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); }Примечание: это намного меньше кода, чем простой шаблонный пользовательский ответ итератора.
вы можете получить это для работы с GCC с помощью
typeofвместоdecltype, но у меня нет этого компилятора под рукой на данный момент, чтобы убедиться, что он компилируется.
Я часто так делаю
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)) {}или если не подряд, но с регулярным шагом (например, битовые флаги)
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)) {}
один из ответов говорит:"Если бы вы знали, что значения перечисления были последовательными, например Qt: Key enum".
Qt:: Key values не являются последовательными. Некоторые сегменты в перечислении есть.
этот поток об итерации по всем значениям в перечислении. Это на самом деле возможно в Qt благодаря использованию Метаобъектной системы:
const QMetaObject *metaObject = qt_getQtMetaObject(); QMetaEnum keyEnum = metaObject->enumerator(metaObject->indexOfEnumerator("Key")); for (int i = 0; i < keyEnum.keyCount(); ++i) { qDebug() << keyEnum.key(i); }см. Также объект QObject::метаобъекта() и Q_ENUM макро.
Я думаю, что такие вещи станут проще с C++20? Но я в это не заглядывал.
если бы вы знали, что значения перечисления были последовательными, например Qt: Key enum, вы могли бы:
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); } }это работает, как ожидалось.
просто сделать массив ints и цикл по массиву, но сделать последний элемент сказать -1 и использовать его для условия выхода.
Если enum-это:
enum MyEnumType{Hay=12,Grass=42,Beer=39};затем создать массив:
int Array[] = {Hay,Grass,Beer,-1}; for (int h = 0; Array[h] != -1; h++){ doStuff( (MyEnumType) Array[h] ); }это не ломается независимо от ints в представлении, пока проверка -1 не сталкивается с одним из элементов, конечно.
Comments