Why do people use enums in C++ as constants while they can use const?

33,740

Solution 1

An enumeration implies a set of related constants, so the added information about the relationship must be useful in their model of the problem at hand.

Solution 2

Bruce Eckel gives a reason in Thinking in C++:

In older versions of C++, static const was not supported inside classes. This meant that const was useless for constant expressions inside classes. However, people still wanted to do this so a typical solution (usually referred to as the “enum hack”) was to use an untagged enum with no instances. An enumeration must have all its values established at compile time, it’s local to the class, and its values are available for constant expressions. Thus, you will commonly see:

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

Solution 3

There's a historical reason too when dealing with template metaprogramming. Some compilers could use values from an enum, but not a static const int to instantiate a class.

template <int N>
struct foo
{
    enum { Value = foo<N-1>::Value + N };
};

template <>
struct foo<0>
{
    enum { Value = 0; }
};

Now you can do it the more sensible way:

template <int N>
struct foo
{
    static const int Value = foo<N-1>::Value + N;
};

template <>
struct foo<0>
{
    static const int Value = 0;
};

Another possible reason, is that a static const int may have memory reserved for it at runtime, whereas an enum is never going to have an actual memory location reserved for it, and will be dealt at compile time. See this related question.

Solution 4

I like the automatic behavior that can be used with enums, for example:

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

Then it is easy to loop until LAST, and when a new state (or whatever is represented) is added, the logic adapts.

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

Add something...

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

The loop adapts...

Solution 5

Enums are more descriptive when used. Consider:

int f(int fg, int bg)

versus

 int f(COLOR fg, COLOR bg)

In addition, enums give a bit more type-safety, because

  • integers are not implicitly convertible to enum types
  • enum of one type is not implicitly convertible to enum of another type
Share:
33,740
Loai Abdelhalim
Author by

Loai Abdelhalim

Senior full-stack developer

Updated on March 30, 2021

Comments

  • Loai Abdelhalim
    Loai Abdelhalim about 3 years

    Why do people use enums in C++ as constants when they can use const?

  • ASk
    ASk almost 15 years
    enum is an integral type, I don't believe it can store a constant such as Pi without significant loss of precision :)
  • ASk
    ASk almost 15 years
    Non-static constants can't be initialized that way. I believe you meant "static const int Val1". Also, there are drawbacks in using this method of in-class initialization - the symbol for Val1 is not guaranteed to be created.
  • dss539
    dss539 almost 15 years
    they might store 314159 and just div by 10^5 every time :P hopefully the example gives the right idea about what I meant by global constants, however.
  • Loai Abdelhalim
    Loai Abdelhalim almost 15 years
    It's not the only reason. Also, there's no big difference in typing.
  • Jan Hudec
    Jan Hudec almost 13 years
    Hm, that does not look like an answer to the question (though it was accepted). People use enums where they could use const and the values are not related. Often you'll see enum { SOMETHING = 2232; } (like that; unnamed enum with one value only) instead of const int SOMETHING = 2232;. It's because enum never gets any storage while const variable is still a variable and will get (static) storage if the compiler can't proove it won't need one, which it often can't.
  • Jan Hudec
    Jan Hudec almost 13 years
    @ASk: The problem is the symbol for "static const int Val1" is not guaranteed to not be generated, so the programmer uses enum instead, because that does not create symbol at all.
  • motiz88
    motiz88 over 10 years
    This is an old thread, but the two statements at the end of this answer are false and need correcting.
  • motiz88
    motiz88 over 10 years
    "integers are not implicitly convertible to enum types" and "enum of one type is not implicitly convertible to enum of another type" - false. Plain enums will silently convert to/from integers, in C++ as in C. So they strictly don't provide this safety. However, in C++11, there is enum class which is a proper C++ style "re-imagining" of enum: The implicit conversions are gone, and additionally value names must always be qualified. This is similar to C# enums, and avoids the name collision / namespace pollution issues that arise with C's old enum.
  • weberc2
    weberc2 almost 10 years
    typedef int my_new_fangled_type; void foo(my_new_fangled_type bar); Problem solved, no enum needed, and I can change the memory representation by replacing typedef int with typedef uchar or some such. :)
  • Jon Watte
    Jon Watte over 7 years
    Another reason is that "const" POD variables are a newer C++ feature, and a lot of older code bases (and programmers who prefer the higher compatibility) still exist.
  • cosimo193
    cosimo193 almost 7 years
    @motiz88 - I think you may be getting confused on this one. Try this code on gcc (you'll need to reformat it yourself): 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; } Enum values do implicitly convert to integers, but not vice-versa, and "enum types" don't implicitly convert between each other. In principle I believe ASk is correct here, but I don't think it's actually an answer to the op's question!
  • cosimo193
    cosimo193 almost 7 years
    Unless someone accidentally adds the new enum literal to the end of the list, or decided to give non-contiguous values to the literals (e.g. enum {} NONE = 0, START = 4, HEY = 7 etc....).