пространства имен для типов перечислений-рекомендации


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

Я ищу плюсы и минусы всех трех подходов.

пример:

// 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: //...
        //...
    }
  }
8 92

8 ответов:

оригинальный C++03 ответ:

The пользу С namespace (через class), что вы можете использовать using заявления, когда вы хотите.

The

FYI в C++0x есть новый синтаксис для случаев, подобных тому, что вы упомянули (см. C++0x wiki страница)

enum class eColors { ... };
enum class eFeelings { ... };

Я бы определенно избегал использования класса для этого; вместо этого используйте пространство имен. Вопрос сводится к тому, следует ли использовать пространства имен или использовать уникальные идентификаторы для перечисления. Лично я бы использовал пространство имен, чтобы мои идентификаторы могли быть короче и, надеюсь, более понятными. Затем код приложения может использовать директиву "using namespace" и сделать все более читаемым.

из вашего примера выше:

using namespace Colors;

void setPenColor( const e c ) {
    switch (c) {
        default: assert(false);
        break; case cRed: //...
        break; case cBlue: //...
        //...
    }
}

Я гибридизировал предыдущие ответы на что - то вроде этого: (EDIT: это полезно только для pre-C++11. Если вы используете C++11, использование enum class)

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

The struct избегает публики: синтаксический сахар, и typedef позволяет фактически объявлять переменные из них перечисления внутри других классов работников.

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

    struct KeySource {
        typedef enum { 
            None, 
            Efuse, 
            Bbram
        } Type;
    };

    struct Checksum {
        typedef enum {
            None =0,
            MD5 = 1,
            SHA1 = 2,
            SHA2 = 3
        } Type;
    };

    struct Encryption {
        typedef enum {
            Undetermined,
            None,
            AES
        } Type;
    };

    struct File {
        typedef enum {
            Unknown = 0,
            MCS,
            MEM,
            BIN,
            HEX
        } Type;
    };

...

class Worker {
    File::Type fileType;
    void DoIt() {
       switch(fileType) {
       case File::MCS: ... ;
       case File::MEM: ... ;
       case File::HEX: ... ;
    }
}

преимущество использования класса заключается в том, что вы можете построить полноценный класс поверх него.

#include <cassert>

class Color
{
public:
    typedef enum
    {
        Red,
        Blue,
        Green,
        Yellow
    } enum_type;

private:
    enum_type _val;

public:
    Color(enum_type val = Blue)
        : _val(val)
    {
        assert(val <= Yellow);
    }

    operator enum_type() const
    {
        return _val;
    }
};

void SetPenColor(const Color c)
{
    switch (c)
    {
        case Color::Red:
            // ...
            break;
    }
}

как показано в приведенном выше примере, с помощью класса, вы можете:

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

просто обратите внимание, что вам нужно объявить operator enum_type() чтобы C++ знал, как преобразовать ваш класс в базовое перечисление. В противном случае вы не сможете передать тип a switch заявление.

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

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

class Colors {
public:
  enum TYPE {
    Red,
    Green,
    Blue
  };
};

template <typename T> void foo (T t) {
  typedef typename T::TYPE EnumType;
  // ...
}

лично я не фанат используя, и я предпочитаю полные имена, поэтому я действительно не вижу в этом плюса для пространств имен. Однако это, вероятно, не самое важное решение, которое вы примете в своем проекте!

поскольку перечисления ограничены их охватывающей областью, вероятно, лучше всего обернуть их в что-то чтобы избежать загрязнения глобального пространства имен, и, чтобы избежать конфликтов имен. Я предпочитаю пространство имен для класса просто потому, что namespace чувствует себя как мешок холдинг, в то время как class ощущается как надежный объект (ср. элемент struct и class обсуждения). Возможное преимущество пространства имен заключается в том, что его можно расширить позже-полезно, если вы имеете дело со сторонним кодом, который вы не можете модифицировать.

это все спорно, конечно, когда мы получаем классы перечисления с C++0x.

Я также, как правило, обернуть мои перечисления в классах.

у меня есть специальный класс toolbox::Enum для моих нужд, который я специализируюсь на каждом шаблоне, который предоставляет основные функции (в основном: отображение значения enum в строку std::, чтобы I/O было легче читать).

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

typedef enum { False: 0, True: 2 } boolean;
   // The classic enum you don't want to see around your code ;)

int main(int argc, char* argv[])
{
  boolean x = static_cast<boolean>(1);
  return (x == False || x == True) ? 0 : 1;
} // main

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

так же:

typedef enum { Zero: 0, One: 1, Two: 2 } example;

int main(int argc, char* argv[])
{
  example y = static_cast<example>(3);
  return (y == Zero || y == One || y == Two) ? 0 : 1;
} // main

еще раз main вернет ошибку.

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

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

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