Объявление перечисления в классе

В следующем фрагменте кода перечисление Color объявляется в классе Car, чтобы ограничить область перечисления и попытаться не "загрязнять" глобальное пространство имен.

class Car
{
public:

   enum Color
   {
      RED,
      BLUE,
      WHITE
   };

   void SetColor( Car::Color color )
   {
      _color = color;
   }

   Car::Color GetColor() const
   {
      return _color;
   }

private:

   Car::Color _color;

};

(1) Является ли это хорошим способом ограничить область перечисления Color? Или я должен объявить его за пределами класса Car, но, возможно, в пределах своего собственного пространства имен или структуры? Я только что наткнулся на эту статью сегодня, которая защищает последнего и обсуждает некоторые интересные моменты о перечислениях: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums.

(2) В этом примере, когда вы работаете в классе, лучше всего кодировать перечисление как Car::Color или просто достаточно Color? (Я предполагаю, что первое лучше, на всякий случай, если в глобальном пространстве имен будет указано другое перечисление Color. Таким образом, по крайней мере, мы явно указываем на перечисление, на которое мы ссылаемся.)

Ответ 1

  • Если Color - это то, что характерно только для Car, тогда вы ограничите его область. Если у вас будет другой перегородчик Color, который используют другие классы, вы также можете сделать его глобальным (или, по крайней мере, внешним Car).

  • Это не имеет значения. Если есть глобальный, тогда локальный все равно используется, так как он ближе к текущей области. Обратите внимание: если вы определяете эту функцию за пределами определения класса, вам нужно явно указать Car::Color в интерфейсе функции.

Ответ 2

В настоящее время - с использованием С++ 11 вы можете использовать enum class для этого:

enum class Color { RED, BLUE, WHITE };

AFAII делает именно то, что вы хотите.

Ответ 3

Я предпочитаю следующий подход (код ниже). Он решает проблему загрязнения пространства имен, но также гораздо более типичен (вы не можете назначать и даже сравнивать два разных перечисления, или перечисление с другими встроенными типами и т.д.).

struct Color
{
    enum Type
    {
        Red, Green, Black
    };
    Type t_;
    Color(Type t) : t_(t) {}
    operator Type () const {return t_;}
private:
   //prevent automatic conversion for any other built-in types such as bool, int, etc
   template<typename T>
    operator T () const;
};

Использование:

Color c = Color::Red;
switch(c)
{
   case Color::Red:
     //некоторый код
   break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error

Я создаю макрос для облегчения использования:

#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
   enum type \
   { \
      BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
   }; \
   type v; \
   EnumName(type v) : v(v) {} \
   operator type() const {return v;} \
private: \
    template<typename T> \
    operator T () const;};\

#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
    BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),

Использование:

DEFINE_SIMPLE_ENUM(Color,
             ((Red, 1))
             ((Green, 3))
             )

Некоторые полезные ссылки:

  • Herb Sutter, Jum Hyslop, Журнал пользователей C/С++, 22 (5), май 2004 г.
  • Херб Саттер, Дэвид Э. Миллер, Бьярн Страуступ Сильно типизированные перечисления (версия 3), июль 2007 г.

Ответ 4

В общем, я всегда помещаю свои перечисления в struct. Я видел несколько рекомендаций, включая "префикс".

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

Всегда считалось, что это больше похоже на C рекомендации, чем C++ (для одного из-за аббревиатуры, а также из-за пространств имен в C++).

Итак, чтобы ограничить область применения, мы имеем две альтернативы:

  • Пространство имен
  • Структуры/классы

Я лично предпочитаю использовать struct, потому что его можно использовать в качестве параметров для программирования шаблонов, в то время как пространство имен нельзя манипулировать.

Примеры манипуляции включают:

template <class T>
size_t number() { /**/ }

который возвращает число элементов перечисления внутри struct T:)

Ответ 5

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