Как я могу перебирать перечисления?



Я только что заметил, что вы не можете использовать стандартные математические операторы на перечисление, такие как ++ или +=



Итак, каков наилучший способ перебора всех значений в перечислении C++?

524   16  

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? Но я в это не заглядывал.

C++ не имеет самоанализа, поэтому вы не можете определить такие вещи во время выполнения.

если бы вы знали, что значения перечисления были последовательными, например 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

    Ничего не найдено.