Enum vs static consts

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

Вариант 1:

namespace Direction
{
    enum Direction
    {
        north,
        east,
        south,
        west,
    };
}

Вариант 2:

namespace Direction
{
    static const unsigned char north = 0;
    static const unsigned char east = 1;
    static const unsigned char south = 2;
    static const unsigned char west = 3;
}

Оба имеют свои преимущества и недостатки.

Pro enum:

  • некоторые типы безопасности:

    void foo(Direction dir); // the compiler won't allow you to just pass an int or a value of an unrelated enum without explicitly casting it
    

Contra перечисление:

  • Безопасность
  • довольно ограничена:

    enum A
    {
        Entry1 = 1
    };
    
    enum B
    {
        Entry2 = 10
    };
    
    A a;
    if(a == Entry2)
        ; // ouch!
    
  • нет поддержки для любого другого типа, кроме int - до С++ 11 нельзя просто перечислить, например, long long, short или char

  • пространство имен для перечислений субоптимально

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

    • если вы переносите enum в отдельное пространство имен, тогда возникает некоторая избыточность при использовании самого перечисления как типа: Затем нужно объявить переменную направления в способе Direction:: Direction (если не использовать "Direction:: Direction", что позволит им снова загрязнить внешнее пространство имен (наконец, в той части кода, где использование директива вступает в силу)), чтобы получить возможность пространства имен своих членов в пути Direction:: north вместо северного

Pro static const:

  • поддержка более высокого типа в С++ до С++ 11 - можно использовать, например, unsigned char для констант
  • правильное определение области - отсутствие загрязнения внешнего пространства имен, без явного запроса его с помощью директивы using (и даже тогда только в ограниченном объеме)

Contra static const:

  • даже меньше безопасности типа, чем перечисления - один больше не может объявить прототип функции следующим образом:

    void foo(Direction dir);
    

    но нужно будет сделать это следующим образом:

    void foo(unsigned char dir); // now each variable of type unsigned char or of a type that can implicitly be casted into unsigned char can be passed, even if its totally unrelated to the expected "enumeration" or if the value does not match the value of any of the expected consts
    

EDIT: Здесь я нашел интересную статью об ограничении в типе безопасности перечислений: http://www.drdobbs.com/enumerations/184401797

Ответ 1

Большая разница - это типизация. Перечисление имеет отдельный тип; статический const должен иметь существующий интегральный тип (хотя его можно использовать в другом месте). Какой из них предпочтительнее что вы хотите: в случае Direction вы, вероятно, захотите уникальный тип, и перечисление предпочтительнее. В других случаях, что вы действительно хотите, это именованная константа с интегральным типом, скажем, как размерность массива. В этих случаях статическая константа вероятно, предпочтительнее.

Ответ 2

С++ 11 решает все точки перехвата с помощью строго типизированных перечислений и улучшенного охвата.

Для фиксированного набора значений, таких как ваш пример кардинальных направлений, я бы использовал перечисление.

Ответ 3

С перечислениями вы можете связать все константы, принадлежащие концепции вместе (с уникальным идентификатором С++ с именем enum).

Herb Sutter писал о предпочтении новых стилей стиля (static_cast) вместо C cast. Один из его аргументов (среди прочих) заключался в том, что их можно найти с регулярным выражением в исходном коде.

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