Есть ли известная парадигма для итерации значений enum?

У меня есть код С++, в котором объявлено следующее перечисление:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma,
   Some_Total
};
int array[Some_Total];

Значения Alpha, Beta и Gamma являются последовательными, и я с удовольствием использую следующий цикл для итерации через них:

for ( int someNo = (int)Some_Alpha; someNo < (int)Some_Total; ++someNo ) {}

Этот цикл в порядке, пока я не решит изменить порядок объявлений в перечислении, скажем, сделать Beta первым значением, а Alpha - вторым. Это делает недействительным заголовок цикла, потому что теперь мне нужно перебирать с Beta на Total. Итак, каковы наилучшие методы повторения через enum? Я хочу перебирать все значения без изменения заголовков циклов каждый раз. Я могу думать о одном решении:

enum Some 
{
   Some_Start = -1,
   Some_Alpha,
   ...
   Some_Total
};
int array[Some_Total];

и итерации от (Start + 1) до Total, но это кажется уродливым, и я никогда не видел, чтобы кто-то делал это в коде. Есть ли известная парадигма для повторения через перечисление, или мне просто нужно исправить порядок значений перечисления? (пусть притворяется, у меня действительно есть некоторые удивительные причины для изменения порядка значений enum)...

Ответ 1

enum Some
{
   Some_first_ = 0,
   Some_Alpha = Some_first_,
....
   Some_last_
};

Выполняя такое вы можете предоставить первый и последний, никогда не меняет порядок

Ответ 2

Вы можете определить operator++() для вашего enum. Это имеет то преимущество, что использует известную парадигму стандартных операторов инкрементации. :)

В зависимости от того, являются ли ваши перечисления смежными, вы можете рассматривать их как int или использовать переключатель:

Some& operator++(Some& obj)
{
# if YOUR_ENUMS_ARE_CONTIGUOUS
  int i = obj;
  if( ++i > Some_Total ) i = Some_Alpha;
  return obj = static_cast<Some>(i);
# else 
  switch(obj)
  {
    case Some_Alpha : obj = Some_Beta;  break;
    case Some_Beta  : obj = Some_Gamma; break;
    case Some_Gamma : obj = Some_Total; break;
    case Some_Total : obj = Some_Alpha; break;
    default: assert(false); // changed enum but forgot to change operator
  }
  return obj;
# endif
}

Обратите внимание, что если operator++() определен, пользователи, вероятно, ожидают и operator--().

Ответ 3

Нет, нет никакого способа сделать это, потому что нет гарантии, что кто-то не написал такой код:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma = 42,
   Some_Delta, 
  Some_Total
};

Ответ 4

Вы можете проверить эту статью с ее исходный код о том, как вы можете реализовать это со статическими членами класса.

Ответ 5

В С++ 11 (и, возможно, ранее) вы можете использовать следующий хак, чтобы сделать Some iterable:

Some operator++(Some& s) {
    return s = (Some )(std::underlying_type<Some>::type(x) + 1); 
}
Some operator*(Some s) {
    return s;
} 
Some begin(Some s) {
    return Some_Alpha;

Some end(Some s) {
    return Some_Gamma;
}

int main() { 
    // the parenthesis here instantiate the enum
    for(const auto& s : Some()) { 
        // etc. etc.
    }
    return 0;
}

(Этот ответ был бесстыдно адаптирован из здесь.)

Ответ 6

Если вы не используете никаких назначений, перечисления гарантированно будут последовательно, начиная с 0 в качестве первого. Про ч ее.

Лучшее, что вы можете сделать, это сохранить их в том порядке, в котором вы хотите, в определении вашего перечисления, и прокручивать их с помощью цикла for.

Ответ 7

Я помещаю все Enums в собственное пространство имен. Пример:

namespace Foo {
enum Enum {
    First=0, // must be sequential
    Second,
    Third,
    End // must be last
};
}

В коде:

for (int i=Foo::First; i!=Foo::End; i++) {
// do stuff
}

Это потому, что С++ разрешает такие вещи (не тестируемые в компиляторе):

enum Foo {
Alpha = 1
};

enum Bar {
Beta = 2
};

Foo foo = Beta;

Это явно неправильно.