Почему люди используют перечисления в C ++ как константы, когда они могут использовать const
?
Почему люди используют перечисления в C ++ как константы, в то время как они могут использовать константу?
Ответы (12)
Перечисление подразумевает набор связанных констант, поэтому добавленная информация о взаимосвязи должна быть полезной в их модели рассматриваемой проблемы.
const
, а значения не связаны. Часто вы увидите enum { SOMETHING = 2232; }
(например, безымянное перечисление только с одним значением) вместо const int SOMETHING = 2232;
. Это потому, что enum никогда не получает никакого хранилища, в то время как переменная const по-прежнему является переменной и получит (статическое) хранилище, если компилятор не сможет доказать, что он не понадобится, что часто бывает невозможно.
- person Jan Hudec; 12.05.2011
Брюс Экель объясняет причину в Мышлении на C ++:
В более старых версиях C ++
static const
не поддерживался внутри классов. Это означало, чтоconst
был бесполезен для постоянных выражений внутри классов. Однако люди по-прежнему хотели это сделать, поэтому типичным решением (обычно называемым «enum hack») было использование нетегированногоenum
без экземпляров. Все значения перечисления должны быть установлены во время компиляции, они являются локальными для класса, а его значения доступны для константных выражений. Таким образом, вы обычно увидите:
#include <iostream> using namespace std; class Bunch { enum { size = 1000 }; int i[size]; }; int main() { cout << "sizeof(Bunch) = " << sizeof(Bunch) << ", sizeof(i[1000]) = " << sizeof(int[1000]) << endl; }
Перечисления - это разные типы, поэтому вы можете делать с ними типизированные вещи, например перегрузку:
enum Color { Red,Green,Blue };
enum Size { Big,Little };
void f( Color c ) {
}
void f( Size s ) {
}
int main() {
f( Red );
f( Big );
}
Есть и историческая причина, когда дело касается метапрограммирования шаблонов. Некоторые компиляторы могут использовать значения из перечисления, но не статическую const int для создания экземпляра класса.
template <int N>
struct foo
{
enum { Value = foo<N-1>::Value + N };
};
template <>
struct foo<0>
{
enum { Value = 0; }
};
Теперь вы можете сделать это более разумным способом:
template <int N>
struct foo
{
static const int Value = foo<N-1>::Value + N;
};
template <>
struct foo<0>
{
static const int Value = 0;
};
Другая возможная причина заключается в том, что static const int может иметь память, зарезервированную для нее во время выполнения, тогда как enum никогда не будет иметь фактическую ячейку памяти, зарезервированную для нее, и будет обрабатываться во время компиляции. См. этот связанный вопрос.
При использовании перечисления более информативны. Учитывать:
int f(int fg, int bg)
против
int f(COLOR fg, COLOR bg)
Кроме того, перечисления обеспечивают немного большую безопасность типов, потому что
- целые числа не могут быть неявно преобразованы в типы перечисления
- перечисление одного типа не может быть неявно преобразовано в перечисление другого типа
enum
s будут незаметно преобразовывать в / из целых чисел в C ++, как и в C. Поэтому они строго не обеспечивают такую безопасность. Однако в C ++ 11 есть enum class
, который является правильным переосмыслением стиля C ++ enum
: неявные преобразования исчезли, и, кроме того, имена значений всегда должны уточняться. Это похоже на C # enum
s и позволяет избежать конфликтов имен / проблем загрязнения пространства имен, которые возникают со старым enum
C.
- person motiz88; 12.09.2013
Мне нравится автоматическое поведение, которое можно использовать с перечислениями, например:
enum {NONE, START, HEY, HO, LAST};
Затем легко выполнить цикл до ПОСЛЕДНЕГО, и когда добавляется новое состояние (или что-то еще, что представлено), логика адаптируется.
for (int i = NONE; i < LAST; i++)
{
// Do stuff...
}
Добавьте что-нибудь...
enum {NONE, START, HEY, WEE, HO, LAST};
Петля адаптируется ...
До того, как поставщики компиляторов внедрили стандарт ISO / IEC 14882: 1998 C ++, этот код для определения константы в области класса приводил к ошибке компиляции:
class Foo {
static const int MAX_LEN = 80;
...
};
Если константа является целочисленным типом, непростая задача - определить ее в перечислении внутри класса:
class Foo {
enum {
MAX_LEN = 80
};
...
};
перечисления также могут использоваться как имя типа. Таким образом, вы можете определить функцию, которая принимает перечисление в качестве параметра, что делает более ясным, какие типы значений должны быть указаны в качестве аргументов функции, по сравнению со значениями, определенными как константные переменные, и функцией, принимающей только "int" как аргумент.
Учитывать:
enum my_new_fangled_type {
baz = 0,
meh = 1
};
void foo (my_new_fangled_type bar) // bar can be a value listed in the enum
{
...
}
против:
int const baz = 0;
int const meh = 1;
void foo (int bar) // what are valid values for bar?
{
...
}
typedef int my_new_fangled_type; void foo(my_new_fangled_type bar);
Проблема решена, перечисление не требуется, и я могу изменить представление памяти, заменив typedef int
на typedef uchar
или что-то подобное. :)
- person weberc2; 31.07.2014
Некоторые отладчики будут отображать имя перечисления вместо его значения при отладке. Это может быть очень полезно. Я знаю, что предпочитаю видеть day_of_week = MONDAY
, чем day_of_week = 1
.
Отчасти потому, что старые компиляторы не поддерживали объявление истинной константы класса.
class C
{
const int ARealConstant = 10;
};
так что пришлось сделать это
class C
{
enum { ARealConstant = 10 };
};
По этой причине многие переносимые библиотеки продолжают использовать эту форму.
Другая причина заключается в том, что перечисления можно использовать в качестве удобного синтаксического устройства для организации констант классов на те, которые связаны, и те, которые не связаны.
class DirectorySearcher
{
enum options
{
showFiles = 0x01,
showDirectories = 0x02,
showLinks = 0x04,
};
};
vs
class Integer
{
enum { treatAsNumeric = true };
enum { treatAsIntegral = true };
enum { treatAsString = false };
};
Использование перечисления кратко документирует допустимые варианты и позволяет компилятору применять их.
Если они используют глобальные константы enum store, например, Pi, то я не знаю, какова их цель.
Одна из причин заключается в том, что const
требует большего набора текста:
enum { Val1, Val2, Val3 };
...против...
const int Val1=0, Val2=1, Val3=2;