Перечень вопросов Enum - PullRequest
       91

Перечень вопросов Enum

6 голосов
/ 15 ноября 2009

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

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

Было бы лучше просто поместить это перечисление в его собственный заголовочный файл, и если да, я должен поместить все перечисления в заголовочный файл, чтобы они были согласованными? Есть ли другие решения этой проблемы (которые логичны)?

Ответы [ 7 ]

14 голосов
/ 29 декабря 2014

Начиная с C ++ 11, вы можете использовать enum class (или enum struct - то же самое, объявленное по-разному), где значения перечисления ограничиваются именем перечисления. Например, вот правильное объявление C ++ 11.

enum class token_type {
    open_paren,
    close_paren,
    identifier
};

Однако, чтобы получить доступ к значениям перечисления, вы должны правильно охватить его, используя оператор ::. Следовательно, это допустимое назначение в C ++ 11:

token_type type = token_type::close_paren;

Но это не так:

token_type type = close_paren;

Это решает конфликт имен и означает, что вам не нужно использовать пространство имен контейнера или структуру только для того, чтобы остановить область значений, утечку которых они не должны делать. Это означает, что следующее перечисление может существовать в той же области видимости, что и token_type:

enum class other_enum {
    block,
    thingy,
    identifier
};

Теперь два значения, называемые identifier в двух разных структурах, не будут мешать.

8 голосов
/ 08 января 2010

Я использую вариант того, что делают Майкл и Роджер:

namespace Color
{
   enum Type
   {
      red,
      green,
      blue
   };
};

void paintHouse(Color::Type color) {...}

main()
{
   paintHouse(Color::red);
}

Я считаю Color::Type красивее и самодокументирующимся, чем Color::Color или COLOR::Color. Если вы найдете Color::Type слишком многословным, вы можете использовать Color::T.

Я не префиксирую свои перечисленные значения (т.е. COLOR_RED), потому что пространство имен вокруг перечисления фактически становится префиксом.

Я прекратил использовать соглашение ALL_CAPS для моих констант в области видимости, потому что они конфликтуют с макросами в библиотеках C (например, NULL). Макросы не попадают в области имен, в которых они определены.

4 голосов
/ 15 ноября 2009

если перечисление используется несколькими классами, то я бы сказал, что оно на самом деле не относится к определению одного класса, а относится к пространству имен, в котором находятся эти классы.

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

3 голосов
/ 15 ноября 2009

Я часто помещаю свои перечисления в пространство имен, чтобы различные значения перечисления не загромождали глобальное пространство имен. Я думаю, что это то, что вы пытаетесь сделать, помещая их в класс. Но если они не «вписываются» в класс, пространство имен работает для этой цели почти также:

namespace FooSettings
{
    enum FooSettings
    {
        foo,
        bar
    };
}
typedef enum FooSettings::FooSettings FooSettingsEnum;


int main()
{
    FooSettingsEnum x = FooSettings::foo;
};

У меня есть фрагмент редактора, который создает схему для нового перечисления, учитывая только его имя, включая

typedef enum FooSettings::FooSettings FooSettingsEnum;

Строка, которая создает typedef, так что более легко читать объявления переменных с типом перечисления.

Я подозреваю, что Страуструп сделал бы имена значений перечисления доступными для перечисления, если бы у него была такая возможность, но совместимость с C заставила его руку (это всего лишь предположение - возможно, однажды я посмотрю в D & E и посмотрю, упоминает ли он ничего).

2 голосов
/ 23 августа 2016

Я согласен с Эмилем. Другой вариант, если вы используете C ++ 98, это использовать struct вместо пространства имен, как показано ниже

struct Color
{
   enum Type
   {
    red,
    green,
    blue
   };
};

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

2 голосов
/ 15 ноября 2009

Вы должны поместить enum вне любого класса, если он является общим, но вы все еще можете использовать enum. Поместите его в пространство имен, чтобы перечислители не «просачивались», загромождая пространство имен вашего проекта:

namespace Project { // you already have this, right? :)
  namespace COLOR { // naming styles differ, use what you like
    enum Color {
      red,
      green,
      blue
    };
  }
  using COLOR::Color; // now you can use the type 'normally'

  // examples:
  struct A {
    Color c;
    A() : c(COLOR::red) {}
  };
  void f(Color c) {
    using namespace COLOR;
    // inside this function, we no longer have to prefix COLOR::
    if (c == green) {
      go();
    }
    else if (c == red) {
      stop();
    }
  }
}
0 голосов
/ 15 ноября 2009

Вы можете попытаться переслать объявление перечисления следующим образом:

enum MyEnum;
...