Простейший метод для итерации через непрерывные значения перечисления в С++

Каков предпочтительный простой метод для итерации через enum со смежными значениями в С++? Я нашел предыдущие SO-вопросы по этому вопросу, которые включали создание пользовательских operator++ и т.д., Но это похоже на излишний. Пока лучшее, что я придумал, это:

enum {
   FOO,
   BAR,
   BLECH,
   NUM_ENUMS
} MyEnum;

//for (MyEnum m = FOO; m < NUM_ENUMS; ++m)             // compile error
//    ...

//for (MyEnum m = FOO; m < NUM_ENUMS; m = m + 1)       // compile error
//    ...

for (MyEnum m = FOO; m < NUM_ENUMS; m = MyEnum(m + 1)) // OK ?
    ...

Является ли это разумным с точки зрения стиля кодирования и может ли оно генерировать предупреждения (g++ -Wall ... кажется довольным этим)?

Ответы

Ответ 1

Это действительно безопасно.

Это было бы undefined: MyEnum(int(NUM_ENUMS) + 1), потому что значение для хранения (4) было бы больше, чем может представлять перечисление ([0, 3]); так как вы гарантируете, что m строго ниже NUM_ENUMS, безопасно использовать MyEnum(m + 1).

С другой стороны, обратите внимание, что у вас возникнут проблемы с настраиваемыми перечислениями, такими как:

enum OtherEnum {
    Foo = -1,
    Bar = 2,
    Baz = 8,
    NUM_OTHERENUM
};

поэтому это не общая практика.

Я бы посоветовал сгенерированный массив вместо этого перебрать:

MyEnum const Values[] = { FOO, BAR, BLECH };

Обратите внимание, что он легко генерируется из определения перечисления, а также не позволяет загрязнять интерфейс с бессмысленным значением (по бизнесу).

Ответ 2

Как и Маттиу, это совершенно безопасно, и никак не противоречит стандарту С++.

В качестве побочной ноты перегрузка оператора ++ над перечислением является не только избыточным, но и имеет проблемы, уже высказанные другими (например, если перечисление не является линейным по значениям)

Поэтому вместо определения оператора ++ вам нужен итератор.

Я адаптировал следующее решение от относительно перечислений с помощью deft_code, но немного адаптировалось к вашему примеру.

template< typename T >
class Enum
{
public:
   class Iterator
   {
   public:
  Iterator( int value ) :
     m_value( value )
  { }

  T operator*( void ) const
  {
     return (T)m_value;
  }

  void operator++( void )
  {
     ++m_value;
  }

  bool operator!=( Iterator rhs )
  {
     return m_value != rhs.m_value;
  }

   private:
      int m_value;
   };

};

template< typename T >
typename Enum<T>::Iterator begin( Enum<T> )
{
   return typename Enum<T>::Iterator( (int)T::First );
}

template< typename T >
typename Enum<T>::Iterator end( Enum<T> )
{
   return typename Enum<T>::Iterator( ((int)T::Last) + 1 );
}

enum class MyEnum
{
   FOO,
   BAR,
   BLECH,
   NUM_ENUMS
};

int main()
{
   for( auto e: Enum<MyEnum>() )
   {
      std::cout << ((int)e) << std::endl;
   }
}

Это может быть излишним для вас, но его намного чище. Другой фрагмент кода, но гораздо более элегантный подход - здесь. Возможно, в будущих классах enum, введенных в С++ 11, может быть итератор в стандарте.

update: Поскольку вы обновили нас, что ваши значения смежны и тот факт, что он необходим для C API (?), то вышеуказанное не подходит