【问题标题】:Why do people use enums in C++ as constants while they can use const?为什么人们在 C++ 中使用枚举作为常量,而他们可以使用 const?
【发布时间】:2010-10-28 08:19:45
【问题描述】:

当人们可以使用const 时,为什么人们在 C++ 中使用枚举作为常量?

【问题讨论】:

  • 我知道这是一个非常古老的线程,但你能举个例子吗?当我阅读这个问题时,我在思考从函数返回 int 的代码行,但使用枚举来定义返回值的特定“版本”,例如错误条件,超时条件等。下面的一些答案似乎解决了完全不同的问题。在我看来,以这种方式使用枚举是错误的,除非正如人们所说,您的编译器的功能受限于静态常量。

标签: c++ enums enumeration


【解决方案1】:

Bruce Eckel 在Thinking in 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;
}

【讨论】:

    【解决方案2】:

    枚举是不同的类型,所以你可以做一些面向类型的事情,比如用它们重载:

    enum Color { Red,Green,Blue };
    enum Size { Big,Little };
    
    void f( Color c ) {
    }
    
    void f( Size s ) {
    }
    
    int main() {
        f( Red );
        f( Big );
    }
    

    【讨论】:

    • 这不是问题所问的。问题是:为什么人们会写这样的东西: enum {Margin = 50};
    【解决方案3】:

    枚举意味着一组相关常量,因此添加的有关关系的信息必须在他们手头问题的模型中有用。

    【讨论】:

    • 嗯,这看起来不像是问题的答案(尽管它被接受了)。人们在可以使用const 的地方使用枚举,并且这些值相关。通常你会看到enum { SOMETHING = 2232; }(就像那样;未命名的枚举只有一个值)而不是const int SOMETHING = 2232;。这是因为 enum 永远不会获得任何存储,而 const 变量仍然是一个变量,并且如果编译器无法证明它不需要存储,它将获得(静态)存储,而它通常不能。
    • 另一个原因是“const” POD 变量是较新的 C++ 特性,许多旧代码库(以及更喜欢更高兼容性的程序员)仍然存在。
    【解决方案4】:

    在处理模板元编程时也有一个历史原因。一些编译器可以使用枚举中的值,但不能使用静态 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;
    };
    

    另一个可能的原因是,静态 const int 可能在运行时为其保留了内存,而枚举永远不会为其保留实际的内存位置,而是在编译时处理。见this related question.

    【讨论】:

      【解决方案5】:

      枚举在使用时更具描述性。考虑:

      int f(int fg, int bg)
      

       int f(COLOR fg, COLOR bg)
      

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

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

      【讨论】:

      • 这是一个旧线程,但是这个答案末尾的两个陈述是错误的,需要更正。
      • “整数不能隐式转换为枚举类型”和“一种类型的枚举不能隐式转换为另一种类型的枚举” - false。普通的enums 将在 C++ 中和在 C 中一样静默地转换成整数/从整数转换。所以它们严格提供这种安全性。但是,在 C++11 中,enum classenum 的正确 C++ 风格“重新想象”:隐式转换消失了,另外值名称必须始终被限定。这类似于 C# enums,并且避免了 C 的旧 enum 出现的名称冲突/命名空间污染问题。
      • @motiz88 - 我想你可能对这个感到困惑。在 gcc 上试试这个代码(你需要自己重新格式化): int main() { enum MyEnum1 { VAL11, VAL12, VAL13 };枚举 MyEnum2 { VAL21, VAL22, VAL23, VAL24 }; int int1 = VAL11;我的枚举 1 枚举 1 = 3; MyEnum2 enum2 = VAL11;枚举2 =枚举1; int int2 = enum1;返回0;枚举值确实会隐式转换为整数,但反之亦然,并且“枚举类型”不会在彼此之间隐式转换。原则上我认为 ASk 在这里是正确的,但我不认为它实际上是对操作问题的回答!
      【解决方案6】:

      我喜欢可以与枚举一起使用的自动行为,例如:

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

      然后很容易循环到 LAST,当添加新状态(或任何表示的状态)时,逻辑就会适应。

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

      添加一些东西...

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

      循环适应...

      【讨论】:

      • 除非有人不小心将新的枚举字面量添加到列表的末尾,或者决定给字面量赋予不连续的值(例如 enum {} NONE = 0, START = 4, HEY = 7等等......)。
      【解决方案7】:

      在编译器供应商实施 ISO/IEC 14882:1998 C++ 标准之前,在类范围内定义常量的这段代码导致编译错误:

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

      如果常量是整数类型,一个笨拙的解决方法是在类内的枚举中定义它:

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

      【讨论】:

      • 非静态常量不能这样初始化。我相信你的意思是“静态常量 int Val1”。此外,使用这种类内初始化方法也有缺点——不能保证会创建 Val1 的符号。
      • @ASk: 问题是“static const int Val1”的符号不能保证不会生成,所以程序员改用枚举,因为那不会创建符号。
      【解决方案8】:

      枚举也可以用作类型名称。因此,您可以定义一个将枚举作为参数的函数,与将值定义为 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?
      {
         ...
      }
      

      【讨论】:

      • typedef int my_new_fangled_type; void foo(my_new_fangled_type bar); 问题已解决,不需要枚举,我可以通过将typedef int 替换为typedef uchar 或类似的东西来更改内存表示。 :)
      【解决方案9】:

      某些调试器在调试时会显示枚举名称而不是其值。这会很有帮助。我知道我宁愿看到day_of_week = MONDAY 而不是day_of_week = 1

      【讨论】:

        【解决方案10】:

        部分是因为旧的编译器不支持真正的类常量的声明

        class C
        {
          const int ARealConstant = 10;
        };
        

        所以不得不这样做

        class C
        {
          enum { ARealConstant = 10 };
        };
        

        因此,许多可移植库继续使用这种形式。

        另一个原因是枚举可以用作一种方便的句法设备来将类常量组织成相关的和不相关的

        class DirectorySearcher
        {
          enum options
          {
            showFiles = 0x01,
            showDirectories = 0x02,
            showLinks = 0x04,
          };
        };
        

        class Integer
        {
           enum { treatAsNumeric = true };
           enum { treatAsIntegral = true };
           enum { treatAsString = false };
        };
        

        【讨论】:

          【解决方案11】:

          使用枚举以简洁的方式记录有效选择,并允许编译器强制执行。

          如果他们使用枚举存储全局常量,例如 Pi,那么我不知道他们的目标是什么。

          【讨论】:

          • enum 是一个整数类型,我不相信它可以存储诸如 Pi 之类的常量而不会显着降低精度:)
          • 他们可能会存储 314159 并且每次只 div 10^5 :P 希望这个示例能够正确理解我所说的全局常量的含义。
          【解决方案12】:

          一个原因是const 需要更多输入:

          enum { Val1, Val2, Val3 };
          

          ...与...

          const int Val1=0, Val2=1, Val3=2;
          

          【讨论】:

          • 这不是唯一的原因。此外,打字也没有太大区别。
          猜你喜欢
          • 2019-10-23
          • 1970-01-01
          • 2011-10-22
          • 2014-05-14
          • 2017-02-22
          • 1970-01-01
          • 2011-01-21
          • 1970-01-01
          • 2011-08-26
          相关资源
          最近更新 更多