Часто требуется несколько перечислимых типов вместе. Иногда возникает конфликт имен. На ум приходят два решения: использовать пространство имен или использовать «более крупные» имена элементов перечисления. Тем не менее, решение с пространством имен имеет две возможные реализации: фиктивный класс с вложенным перечислением или полноценное пространство имен.
Я ищу плюсы и минусы всех трех подходов.
Пример:
// oft seen hand-crafted name clash solution
enum eColors { cRed, cColorBlue, cGreen, cYellow, cColorsEnd };
enum eFeelings { cAngry, cFeelingBlue, cHappy, cFeelingsEnd };
void setPenColor( const eColors c ) {
switch (c) {
default: assert(false);
break; case cRed: //...
break; case cColorBlue: //...
//...
}
}
// (ab)using a class as a namespace
class Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
class Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
// a real namespace?
namespace Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
namespace Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
switch (c) {
default: assert(false);
break; case Colors::cRed: //...
break; case Colors::cBlue: //...
//...
}
}
enum e {...}
, перечисления могут быть анонимными, т. Е. Чтоenum {...}
имеет гораздо больший смысл, если оно заключено в пространство имен или класс.Ответы:
Исходный ответ C ++ 03:
Выгода от А
namespace
(большеclass
), что вы можете использоватьusing
объявление , когда вы хотите.Проблема с использованием в
namespace
том , что пространство имен может быть расширен в других местах в коде. В большом проекте вам не будет гарантировано, что два разных перечисления не оба думают, что они вызываютсяeFeelings
Для более простого кода я использую a
struct
, поскольку вы, вероятно, хотите, чтобы его содержимое было общедоступным.Если вы выполняете какую-либо из этих практик, вы на шаг впереди и, вероятно, вам не нужно вдаваться в подробности.
Новее, совет C ++ 11:
Если вы используете C ++ 11 или новее,
enum class
будет неявно ограничивать значения перечисления в пределах имени перечисления.С
enum class
вы потеряете неявные преобразования и сравнения с целочисленными типами, но на практике это может помочь вам обнаружить неоднозначный или ошибочный код.источник
FYI В C ++ 0x есть новый синтаксис для случаев, подобных тому, что вы упомянули (см. Страницу вики C ++ 0x )
источник
Я гибридизировал предыдущие ответы на что-то вроде этого: (EDIT: это полезно только для pre-C ++ 11. Если вы используете C ++ 11, используйте
enum class
)У меня есть один большой файл заголовка, который содержит все мои перечисления проектов, потому что эти перечисления используются совместно между рабочими классами, и нет смысла помещать перечисления в сами рабочие классы.
Он
struct
избегает общедоступного синтаксического сахара иtypedef
позволяет вам фактически объявлять переменные этих перечислений в других рабочих классах.Я не думаю, что использование пространства имен вообще помогает. Возможно, это потому, что я программист на C #, и вам нужно использовать имя типа перечисления при ссылке на значения, поэтому я к нему привык.
...
источник
Я бы определенно избегал использовать для этого класс; вместо этого используйте пространство имен. Вопрос сводится к тому, использовать ли пространство имен или использовать уникальные идентификаторы для значений перечисления. Лично я бы использовал пространство имен, чтобы мои идентификаторы были короче и, надеюсь, более понятными. Тогда код приложения может использовать директиву using namespace и сделать все более читабельным.
Из вашего примера выше:
источник
Colors someColor = Red;
, потому что пространство имен не является типом.Colors::e someColor = Red;
Вместо этого вам придется писать , что довольно противоречит интуиции.Colors::e someColor
даже с a,struct/class
если бы вы хотели использовать его вswitch
выражении? Если вы используете анонимный,enum
то коммутатор не сможет оценитьstruct
.const e c
мне это кажется трудночитаемым :-) Не делайте этого. Однако использование пространства имен - это нормально.Разница между использованием класса или пространства имен состоит в том, что класс нельзя открыть повторно, как это можно сделать с пространством имен. Это позволяет избежать возможности злоупотребления пространством имен в будущем, но также существует проблема, которую вы также не можете добавить в набор перечислений.
Возможное преимущество использования класса заключается в том, что они могут использоваться в качестве аргументов типа шаблона, что не относится к пространствам имен:
Лично я не являюсь поклонником использования и предпочитаю полностью определенные имена, поэтому я не считаю это плюсом для пространств имен. Однако, вероятно, это не самое важное решение, которое вы примете в своем проекте!
источник
Преимущество использования класса в том, что на его основе можно построить полноценный класс.
Как показано в приведенном выше примере, с помощью класса вы можете:
Просто обратите внимание, что вам нужно объявить,
operator enum_type()
чтобы C ++ знал, как преобразовать ваш класс в базовое перечисление. В противном случае вы не сможете передать типswitch
выражению.источник
Поскольку перечисления ограничены их охватывающей областью видимости, вероятно, лучше всего обернуть их чем-то, чтобы избежать загрязнения глобального пространства имен и избежать конфликтов имен. Я предпочитаю пространство имен классу просто потому,
namespace
что оно похоже на сумку для хранения, тогда какclass
ощущается как надежный объект (см. Обсуждениеstruct
vs.class
). Возможное преимущество пространства имен состоит в том, что его можно расширить позже - это полезно, если вы имеете дело со сторонним кодом, который вы не можете изменить.Это все спорно, конечно, когда мы получаем классы перечислений с C ++ 0x.
источник
Я также стараюсь объединять свои перечисления в классы.
Как сообщил Ричард Корден, преимущество класса состоит в том, что это тип в смысле C ++, и поэтому вы можете использовать его с шаблонами.
У меня есть специальный класс toolbox :: Enum для моих нужд, который я специализирую для каждого шаблона, который предоставляет базовые функции (в основном: отображение значения enum в std :: string, чтобы ввод-вывод было легче читать).
Мой маленький шаблон также имеет дополнительное преимущество - действительно проверять допустимые значения. Компилятор не очень хорошо проверяет, действительно ли значение находится в перечислении:
Меня всегда беспокоило, что компилятор этого не поймает, поскольку у вас остается значение enum, которое не имеет смысла (и чего вы не ожидаете).
Так же:
И снова main вернет ошибку.
Проблема в том, что компилятор поместит перечисление в наименьшее доступное представление (здесь нам нужно 2 бита) и что все, что соответствует этому представлению, считается допустимым значением.
Также существует проблема, заключающаяся в том, что иногда вы предпочитаете использовать цикл для возможных значений вместо переключателя, чтобы вам не приходилось изменять все переключатели каждый раз, когда вы добавляете значение в перечисление.
В общем, мой маленький помощник действительно упрощает работу с перечислениями (конечно, он добавляет некоторые накладные расходы), и это возможно только потому, что я вкладываю каждое перечисление в отдельную структуру :)
источник