(Як) я можу порахувати предмети в перерахунку?


98

Це питання мені прийшло в голову, коли у мене було щось подібне

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]якщо я не помиляюся.)


Ця публікація може відповісти на ваше запитання: 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

Я задав дуже пов'язану річ на stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol

Я б порекомендував вам рішення від stackoverflow.com/a/60216003/12894563 і поліпшеного варіанта з stackoverflow.com/a/60271408/12894563
ixjxk

Відповіді:


123

Насправді це не дуже вдалий спосіб зробити це, як правило, ви бачите додатковий предмет у перерахунку, тобто

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

Тоді ви можете зробити:

int fuz[FOOBAR_NR_ITEMS];

Все ще не дуже приємно, хоча.

Але, звичайно, ви розумієте, що просто кількість предметів у перерахунку не є безпечною, якщо це стосується, наприклад

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

кількість елементів буде 4, але цілі значення значень enum будуть виходити з діапазону індексу масиву. Використання значень enum для індексації масиву не є безпечним, слід розглянути інші варіанти.

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


27
Назвіть це ОСТАННЕ або ВЖЕ_за_END_END чи щось не так кричуще. Зробіть його стирчати . Щоб наступні технічні працівники випадково не додавали нові записи після закінчення маркера.
Мартін Йорк

3
На краще чи гірше, це такий підхід, який ми застосовуємо в нашій організації. Ми зазвичай називаємо це FINAL_enumname_ENTRY, наприклад FINAL_foobar_ENTRY. Я також бачив, як люди використовують окрему статичну змінну const FOOBAR_COUNT, визначену відразу після декларування enum, підхід, який трохи більше схильний до помилок.
Дарріл

1
Принаймні, досить легко побачити, що "enum foo {a = 10, LAST}" буде дивним. І я подумав, що "int arr [LAST]" буде 11 предметів у цьому випадку, а не 2, тому більшість кодів спрацює (але ви витрачаєте пам’ять на недійсні значення індексу)
Кодовик

32

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

Найпростіший підхід, який працює як на C, так і на C ++, - це прийняти конвенцію про декларацію значення ... MAX для кожного з ваших типів enum:

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дає максимально можливе значення перерахунку).
  2. Особисто я відчуваю, що Folders_MAX = FCвиділяється з інших записів трохи більше (що робить дещо складніше випадкове додавання значень перерахунку без оновлення максимального значення, про що йдеться в проблемі, яку посилається на Мартина Йорка).
  3. GCC включає корисні попередження, такі як "значення перерахунку, не включене в комутатор" для такого коду, як наведено нижче. Якщо дозволити Folders_MAX == FC + 1, це попередження порушує ці попередження, оскільки в кінцевому підсумку ви знайдете безліч ... Значень перерахування MAX, які ніколи не повинні включатися в комутатор.
перемикач (папка) 
{
  випадок FA: ...;
  справа FB: ...;
  // На жаль, забув ФК!
}

3
Чому б не зробити enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Білл

1
Я вважаю за краще struct SomeEnum { enum type {FA, FB, FC, NB__};};
уточнити,

2
Насправді я відчуваю, що ці "корисні" попередження - це біль у дупі. Мені подобаються хороші застереження, які я завжди встановлюю -Wall -pedantic і т.д., коли я розвиваюсь, але ці попередження просто дурні. Є лише кілька гірших, як, наприклад, пропонують паролі для && || і & ^ | пріоритет оператора. Я маю на увазі, я думав, що ява - мова няні, що, до біса, відбувається з C та C ++ ...
що

2
Мінус наявності Folders_max = FC полягає в тому, що ви повинні змінювати його щоразу, коли ви щось додаєте до перерахунків!
Етьєнн

2
перелік папок {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Просто підкреслити, що це корисно для ітерації?
gjpc

7

Як щодо ознак STL? Наприклад:

enum Foo
{
    Bar,
    Baz
};

написати анну

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

спеціалізація (можливо constexpr, якщо ви використовуєте c ++ 11). Потім у своєму тестовому коді введіть будь-які статичні твердження для підтримки обмежень, які std :: numeric_limits :: max () = last_item.


2
На жаль, це не спрацює відповідно до цієї відповіді .
rr-

3
std::numeric_limits<enum Foo>::max()завжди повертає нуль ... (див. запитання щодо пов’язаної відповіді) Тестовано на звичайних перерахунках ( enum Foo { ... }), натяканих типу enums ( enum class Foo : uint8_t { ... }) з gcc 5.2.0 @ Linux та MinGW 4.9.3 @ Windows.
rr-

1
(... і у випадку std::numeric_limits<std::underlying_type<Foo>::type>::max(), він повертає максимальне значення базового типу, тобто 0xFFFFFFFF для 32-бітових цілих чисел, що не корисно в цьому контексті.)
rr-

1
Дивно, бо у мене є робочий код, який робить те, що я описав. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Потім: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) І std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;друкується очікуваний результат. Випробуваний на gcc 5.2.1 та 4.8 / 4.9 ароматизаторів.
Войцех Мігда

2
-1; ping мені, якщо ви зміните відповідь, тож я можу скасувати подання заявки. Ця відповідь є поганою умовою, яку слід дотримуватися. Це неправильне використання концепції numeric_limits<T>::max(). Єдине, що може розумно повернути функція, - це найвище перераховане значення. Він би повернувся 2, але ОП (у цьому конкретному випадку) потребував би його для повернення 3. Коли у вас є значення, які не знаходяться за замовчуванням для enum ( FB = 2057), всі ставки вимикаються, навіть не + 1можна прокручувати помилку "за одним". Якби було numeric_limits<T>::number_of_elements_of_the_set()(або коротша назва), це можна було б використовувати беззаперечно.
Мерлін Морган-Грем

3

Додайте запис в кінці вашої перерахунку під назвою Folders_MAX або щось подібне і використовуйте це значення при ініціалізації ваших масивів.

ContainerClass* m_containers[Folders_MAX];

2

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

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

Це не приємно, але це чисте рішення, якщо ви не змінюєте перерахунок, не оновлюючи константу.


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 елементів

Найкращим способом вирішити цю проблему в загальному випадку буде перегляд ваших перерахувань, але це ще не в стандарті, наскільки я знаю

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.