(Как) я могу посчитать предметы в перечислении?
Этот вопрос пришел мне в голову, когда у меня было что-то вроде
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 для последнего допустимого значения перечисления по нескольким причинам:
- Имя константы технически более точно (так как
Folders_MAX
дает максимально возможное значение enum). - Лично я чувствую, что
Folders_MAX = FC
выделяется из других записей чуть больше (что усложняет случайное добавление значений enum без обновления максимального значения, проблема, на которую ссылается Мартин Йорк). - 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;
}