为什么人们在C++中使用枚举作为常量而不是使用const?

35

为什么人们在C++中使用枚举作为常量,而不是使用const


我知道这是一个非常老的帖子,但你能给个例子吗?当我读到这个问题时,我想到的是从函数返回int类型的代码,但使用枚举来定义返回值的特定“版本”,例如错误条件、超时条件等。下面的一些答案似乎完全没有涉及到这个问题。在我看来,除非像人们所说的那样,你的编译器在静态常量方面的功能受到限制,否则以这种方式使用枚举是错误的。 - cosimo193
可能是Should I use #define, enum or const?的重复问题。 - phuclv
12个回答

50

Bruce Eckel在C++ 编程思想中给出了一个理由:

在旧版本的 C++ 中,类内不支持 static const。这意味着在类内部,const 对于常量表达式是无效的。然而,人们仍然希望这样做,所以通常的解决方案(通常称为“枚举黑科技”)是使用没有实例的未命名 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;
}

36
枚举是独特的类型,因此您可以使用它们进行面向类型的操作,例如重载:
enum Color { Red,Green,Blue };
enum Size { Big,Little };

void f( Color c ) {
}

void f( Size s ) {
}

int main() {
    f( Red );
    f( Big );
}

9
这不是问题所问的。问题是:为什么人们会写出像这样的东西:enum {Margin = 50}; - Claudio

25

枚举是一组相关的常量,因此关于它们之间的关系的附加信息必须对他们手头的问题模型有用。


37
这似乎并不是对问题的回答(尽管它被接受了)。人们在可以使用“const”的情况下使用枚举,而这些值与枚举本身无关。通常你会看到这样的写法:enum { SOMETHING = 2232; }(类似这样;未命名的仅有一个值的枚举),而不是const int SOMETHING = 2232;。这是因为枚举永远不会得到任何存储空间,而常量变量仍然是变量,如果编译器无法证明它不需要一个变量,它将获得(静态)存储空间,而这种情况经常发生。 - Jan Hudec
1
另一个原因是“const” POD 变量是较新的 C++ 特性,许多旧代码库(和偏好更高兼容性的程序员)仍然存在。 - Jon Watte

22

在处理模板元编程时,还有一个历史原因。一些编译器可以使用枚举中的值来实例化类,但不能使用静态常量整数。

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;
};

另一个可能的原因是,静态常量整数可能在运行时为其保留内存,而枚举类型永远不会有实际的内存位置保留,将在编译时处理。请参见此相关问题


10

我喜欢使用枚举类型时能够自动执行的行为,例如:

enum {NONE, START, HEY, HO, LAST};

然后,只需循环直到LAST,当添加新状态(或其他所代表的内容)时,逻辑会自适应。

for (int i = NONE; i < LAST; i++)
{
    // Do stuff...
}

添加一些内容...

enum {NONE, START, HEY, WEE, HO, LAST};

循环会根据所处理的输入数据进行适应...


2
除非有人意外地将新的枚举常量添加到列表末尾,或者决定为这些常量赋予不连续的值(例如:枚举{} NONE = 0,START = 4,HEY = 7等)。 - cosimo193

9

当使用时,枚举更具描述性。考虑以下例子:

int f(int fg, int bg)

相对于

 int f(COLOR fg, COLOR bg)

此外,枚举类型提供了更多的类型安全性,因为:

  • 整数不能隐式转换为枚举类型
  • 一个枚举类型不能隐式转换为另一个枚举类型

这是一个旧的帖子,但是这个答案末尾的两个声明是错误的,需要进行更正。 - motiz88
5
"integers are not implicitly convertible to enum types" 和 "enum of one type is not implicitly convertible to enum of another type" - 错误。 在 C++ 中,与 C 语言一样,普通的枚举类型可以隐式地转换为整数类型。因此,它们严格来说并不提供这种安全性。 然而,在 C++11 中,引入了 enum class 类型,是一个正统的 C++ 风格的 "重新构想" 枚举类型:隐式转换被禁止,此外值必须始终带有限定名称(qualified name)。这类似于 C# 的枚举类型,并且避免了 C 语言旧版枚举类型中出现的名称冲突和命名空间污染问题。 - motiz88
@motiz88 - 我认为你可能对这个问题感到困惑了。在gcc上尝试这段代码(你需要自己重新格式化):int main() { enum MyEnum1 { VAL11, VAL12, VAL13 }; enum MyEnum2 { VAL21, VAL22, VAL23, VAL24 }; int int1 = VAL11; MyEnum1 enum1 = 3; MyEnum2 enum2 = VAL11; enum2 = enum1; int int2 = enum1; return 0; }枚举值隐式转换为整数,但反之则不然,“枚举类型”之间也不会隐式转换。原则上,我认为ASk在这里是正确的,但我不认为这实际上是对op问题的回答! - cosimo193

5

在编译器供应商实现ISO/IEC 14882:1998 C++标准之前,这段代码在类作用域中定义常量会导致编译错误:

class Foo {
    static const int MAX_LEN = 80;
    ...
};

如果常量是整数类型,一种巧妙的解决方法是在类内部定义一个枚举:

class Foo {
    enum {
        MAX_LEN = 80
    };
    ...
};

1
非静态常量不能以这种方式初始化。我相信你的意思是“static const int Val1”。 此外,使用这种内部类初始化方法存在缺点 - 无法保证Val1的符号被创建。 - ASk
1
@ASk:问题在于“static const int Val1”的符号不能保证不会被生成,因此程序员使用枚举而不是它,因为枚举根本不会创建符号。 - Jan Hudec

4

枚举类型也可以用作类型名称。因此,您可以定义一个以枚举为参数的函数,这使得清楚了解应该将哪些值作为函数参数给出,与将值定义为const变量并且函数接受"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?
{
   ...
}

1
定义一个新的类型 my_new_fangled_type 为整型 int,并声明了一个函数 foo,该函数参数为 my_new_fangled_type bar。问题解决了,不需要枚举类型,只需将 typedef int 替换为 typedef uchar 或其他类型即可更改内存表示方式。 :) - weberc2

2

这在一定程度上是因为旧编译器不支持声明真正的类常量。

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 };
};

2

在调试过程中,一些调试器会显示枚举名称而不是其值,这非常有帮助。我知道我宁愿看到 day_of_week = MONDAY 而不是 day_of_week = 1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接