Простейший метод для итерации через непрерывные значения перечисления в С++
Каков предпочтительный простой метод для итерации через 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 (?), то вышеуказанное не подходит