(Как) я могу посчитать предметы в перечислении?

Этот вопрос пришел мне в голову, когда у меня было что-то вроде

enum Folders {FA, FB, FC};

и хотел создать массив контейнеров для каждой папки:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(При использовании карт гораздо удобнее использовать: std::map<Folders, ContainerClass*> m_containers;)

Но вернемся к моему первоначальному вопросу: что, если я не хочу жестко задавать размер массива, есть ли способ выяснить, сколько элементов в папках? (Не полагаясь, например, FC будучи последним элементом в списке, который позволил бы что-то вроде ContainerClass*m_containers[FC+1] если я не ошибаюсь.)

7 ответов

Решение

Не очень хороший способ сделать это, обычно вы видите дополнительный элемент в enum, т.е.

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Итак, вы можете сделать:

int fuz[FOOBAR_NR_ITEMS];

Все еще не очень хорошо, хотя

Но, конечно, вы понимаете, что просто количество элементов в перечислении небезопасно, например,

enum foobar {foo, bar = 5, baz, quz = 20};

число элементов будет равно 4, но целочисленные значения значений перечисления будут выходить за пределы диапазона индекса массива. Использование перечислимых значений для индексации массива небезопасно, вы должны рассмотреть другие варианты.

редактировать: по запросу, сделал специальную запись больше.

Для C++ доступны различные методы перечисления, безопасные для типов, и некоторые из них (например, предложенный, но никогда не представленный Boost.Enum) включают поддержку для получения размера перечисления.

Простейший подход, который работает как в C, так и в C++, заключается в принятии соглашения об объявлении значения...MAX для каждого из типов перечислений:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Изменить: Относительно { FA, FB, FC, Folders_MAX = FC} против {FA, FB, FC, Folders_MAX]: Я предпочитаю устанавливать значение MAX для последнего допустимого значения перечисления по нескольким причинам:

  1. Имя константы технически более точно (так как Folders_MAX дает максимально возможное значение enum).
  2. Лично я чувствую, что Folders_MAX = FC выделяется из других записей чуть больше (что усложняет случайное добавление значений enum без обновления максимального значения, проблема, на которую ссылается Мартин Йорк).
  3. GCC содержит полезные предупреждения, такие как "значение перечисления, не включенное в switch" для кода, такого как следующий. Разрешение Folders_MAX == FC + 1 снимает эти предупреждения, так как в итоге вы получаете кучу... значений перечисления MAX, которые никогда не должны включаться в switch.
переключатель (папка) 
{
  дело FA: ...;
  дело FB: ...;
  // Ой, забыл ФК!
}

Как насчет черт в стиле STL? Например:

enum Foo
{
    Bar,
    Baz
};

написать

std::numeric_limits<enum Foo>::max()

специализация (возможно, constexpr, если вы используете C++11). Затем в своем тестовом коде предоставьте все статические утверждения для поддержки ограничений, которые std::numeric_limits::max() = last_item.

Добавьте в конец вашего перечисления запись с именем Folders_MAX или чем-то подобным и используйте это значение при инициализации ваших массивов.

ContainerClass* m_containers[Folders_MAX];

Я действительно не вижу никакого способа реально получить количество значений в перечислении в C++. Любое из упомянутых выше решений работает, если вы не определите значение своих перечислений, если вы определите, что вы можете столкнуться с ситуациями, когда вы создаете массивы слишком большие или слишком маленькие.

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

в приведенных примерах результат будет создавать массив из 3 элементов, когда вам нужен массив из 5 элементов

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

в приведенных примерах результат будет создавать массив из 301 элементов, когда вам нужен массив из 5 элементов

Лучший способ решить эту проблему в общем случае - это перебирать ваши перечисления, но, насколько я знаю, это пока не входит в стандарт.

Мне нравится использовать перечисления в качестве аргументов моих функций. Это простой способ предоставить фиксированный список "опций". Проблема с ответом, получившим наибольшее количество голосов, заключается в том, что с его помощью клиент может указать "недопустимую опцию". В качестве побочного результата я рекомендую делать по существу то же самое, но использовать константу int вне enum для определения их количества.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

Это не приятно, но это чистое решение, если вы не измените enum без обновления константы.

Вот лучший способ сделать это во время компиляции. Я использовал ответ счетчика arg_var отсюда .

      #define PP_NARG(...) \
     PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)

#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#define TypedEnum(Name, ...)                                      \
struct Name {                                                     \
    enum {                                                        \
        __VA_ARGS__                                               \
    };                                                            \
    static const uint32_t Name##_MAX = PP_NARG(__VA_ARGS__);      \
}

#define Enum(Name, ...) TypedEnum(Name, __VA_ARGS__)

Чтобы объявить перечисление:

      Enum(TestEnum, 
Enum_1= 0,
Enum_2= 1,
Enum_3= 2,
Enum_4= 4,
Enum_5= 8,
Enum_6= 16,
Enum_7= 32);

максимум будет доступен здесь:

      int array [TestEnum::TestEnum_MAX];
for(uint32_t fIdx = 0; fIdx < TestEnum::TestEnum_MAX; fIdx++)
{
     array [fIdx] = 0;
}
Другие вопросы по тегам