2010-01-20 2 views
72

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

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], если я не ошибаюсь.)

+0

Это сообщение может ответить на ваш вопрос: http://stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c. – StackedCrooked

+1

Вопрос немного неопределен. По стандарту C++, 'int (FA) | int (FB) | int (FC) 'также является юридическим значением переменной' Folders'. Если вы определяете 'm_containers' так, что любая переменная' Folders' является допустимым индексом, '[FC + 1]' не будет достаточно большим. – MSalters

+0

Я задал очень смелую вещь на http://stackoverflow.com/questions/12972317/count-on-enum-c-automatic – sergiol

ответ

90

Там не очень хороший способ сделать это, как правило, вы увидите дополнительный пункт в перечислении, то есть

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

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

int fuz[FOOBAR_NR_ITEMS]; 

Еще не Очень приятно.

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

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

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

редактирование: в соответствии с запросом, сделана специальная запись.

+24

Назовите это LAST или ALWAYS_AT_END или что-то не так загадочное. Сделайте это __stick out__. Чтобы последующие сопровождающие не случайно добавляли новые записи после окончания маркера. –

+2

К сожалению, это подход, который мы принимаем в нашей организации. Обычно мы называем это FINAL_enumname_ENTRY, например FINAL_foobar_ENTRY. Я также видел, что люди используют отдельную переменную static FOOBAR_COUNT, определенную сразу после объявления перечисления, подход, который немного более подвержен ошибкам. – Darryl

+1

По крайней мере, довольно легко увидеть, что «enum foo {a = 10, LAST}» будет нечетным. И я подумал: «int arr [LAST]» будет 11 пунктов в этом случае, а не 2, поэтому большинство кода будет работать (но вы теряете память на недопустимые значения индекса) –

3

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

ContainerClass* m_containers[Folders_MAX]; 
26

Для C++ существуют различные type-safe enum techniques, а некоторые из них (например, предлагаемые, но никогда не представленные 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 дает макси mum возможное значение enum).
  2. Лично мне кажется, что Folders_MAX = FC выделяется из других записей немного больше (что делает его немного сложнее случайно добавить значения перечисления без обновления максимального значения, на что указывает Мартин Йорк).
  3. GCC содержит полезные предупреждения, такие как «значение перечисления, не включенное в коммутатор» для кода, такого как следующее. Letting Folders_MAX == FC + 1 разрывает эти предупреждения, так как у вас заканчивается куча ...MAX, которые никогда не должны включаться в коммутатор.
 
switch (folder) 
{ 
    case FA: ...; 
    case FB: ...; 
    // Oops, forgot FC! 
} 
+0

Отличное решение, которое я никогда раньше не видел. Я был в середине предлагая взломать Boost.Preprocessor, который я использовал, но это более элегантно (и то, что я искал). –

+3

Почему бы не сделать: 'enum Folders {FA, FB, FC, Folders_MAX}; ContainerClass * m_containers [Folders_MAX]; '? – Bill

+0

Я предпочитаю пояснить, что последнее - это число, и все они имеют одно и то же имя благодаря: 'struct SomeEnum {enum type {FA, FB, FC, NB __};};' –

5

Как насчет признаков, в качестве НТБ моды? Например:

enum Foo 
{ 
    Bar, 
    Baz 
}; 

написать

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

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

+2

К сожалению, это не сработает [этот ответ] (http://stackoverflow.com/a/9201960/2016221). –

+0

Пример, показывающий, когда это произойдет, было бы полезно. –

+2

'std :: numeric_limits :: max()' всегда возвращает ноль ... (см. Вопрос для связанного ответа) Протестировано на обычных перечислениях ('enum Foo {...}'), перечислены типы перечислений (' enum class Foo: uint8_t {...} ') с gcc 5.2.0 @ Linux и MinGW 4.9.3 @ Windows. –

1

Я действительно не вижу никакого способа действительно получить число значений в перечислении в 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 элементов

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

0

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

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

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

Смежные вопросы