views:

395

answers:

9

The typical way to define an integer constant to use inside a function is:

const int NumbeOfElements = 10;

the same for using within a class:

class Class {
...
    static const int NumberOfElements = 10;
};

It can then be used as a fixed-size array bound which means it is known at compile time.

Long ago compilers didn't support the latter syntax and that's why enums were used:

enum NumberOfElementsEnum { NumberOfElements = 10; }

Now with almost every widely used compiler supporting both the in-function const int and the in-class static const int syntax is there any reason to use the enum for this purpose?

A: 

bottom line - use const.

more details:

I'm not a c++ expert, but this seems a more general design question.

If it's not a must, and you think there's a very low/non-existent probability that the enum will grow to have more then one value, then use a regular const. even if you are wrong and at some point in the future there will be more values, making an enum the right choice - a simple refactoring and you change you const to enum.

Ami
This isn't about `enum` vs `const` from high-level design point of view. This is all about C++ quirks in relation to each construct. The obvious answer is wrong here.
Pavel Minaev
+3  A: 

I think that there's no reason to use an enum, and that it's actually better to use a static const int for this purpose, since an enum has its own type (even if implicitly convertible to an integer).

Paolo Tedesco
Not that I mind being downvoted, but it would be nice to let me know why. Thanks.
Paolo Tedesco
Why is it bad that an enum has its own type? Does your program have a type quota that you're not allowed to exceed?
Rob Kennedy
+6  A: 

Defining static constants directly in the class definition is a later addition to C++ and many still stick to the older workaround of using an enum for that. There might even be a few older compilers still in use which don't support static constants directly defined in class definitions.

sbi
Some compilers won't always inline the static const int, whereas the enum will always be inlined AFAIK.
Dan O
+3  A: 

In your case, I'd use a constant as well. However, there are other cases where I might be adding other, related constants. Like this:

const int TextFile = 1; // XXX Maybe add other constants for binary files etc.?

In such cases, I use an enum right away with a single value, like this:

enum FileType {
    TextFile = 1
    // XXX Maybe add other values for binary files etc.?
}

The reason is that the compiler can then issue warnings when I'm using the constant value in switch expressions, as in:

FileType type;
// ...
switch ( type ) {
    case TextFile:
       // ...
}

In case I decide to add another constant value which is related to the existing value (a different type of file, in this example), virtually all compilers will issue a warning since the new value is not handled in the switch statement.

If I had used 'int' and constants instead, the compiler wouldn't have a chance to issue warnings.

Frerich Raabe
+4  A: 

The only reason for using the "enum hack" is that old compilers do not support in-class const definitions, as you say in your question. So, unless you suspect that your code will be ported to an old compiler, you should use const where const is due.

Gorpik
+1  A: 

Well, the portability is a good reason for using enum. It is great, because you don't have to worry whether your compiler supports "static const int S = 10" or not...

Also, as far as I remember, static variable must be defined somewhere, as well as declared, and the enum value must be declared only.

SadSido
A `static const` value of integral type needs not be defined if the declaration includes an integral constant expression initializer, but the rules are very murky there. See my answer.
Pavel Minaev
+2  A: 

Use of enum have one advantage. An enum type is a type, so if you define, for example:

enum EnumType { EnumValue1 = 10, EnumValue2 = 20 ... };

and you have a function like:

void Function1(EnumType Value)

the compiler checks that you are passing a member of the enum EnumType to the function, so only valid values for parameter Value would be EnumValue1 and EnumValue2. If you use constants and change the function to

void Function1(int Value)

the compiler checks that you are passing an int (any int, a constant, variable or literal) to the function.

Enum types are good for grouping related const-values. For only one const value, I do not see any advantage.

jrbjazz
+1  A: 

There is a difference between those two. Enums don't have an adress as far as I know. static const ints do though. So if someone takes the adress of the const static int, casts away the const, he can modify the value (although the compiler might ignore the change because he thinks it's const). This is of course pure evil and you should not do it - but the compiler can't prevent it. This can't happen with enums.

And of course - if you (for some reason) need the adress of that const, you need the static const int.

In short - enum is an rvalue while const static int is an lvalue. See http://www.embedded.com/story/OEG20011129S0065 for more details.

Tobias Langner
The client won't be able to take address of a `static const int` member variable without a definition. Rvalue/lvalue distinction is spot on, though.
Pavel Minaev
The client won't - but anyone maintaining the class and adding code later.
Tobias Langner
+8  A: 

The reason is mainly brevity. First of all, an enum can be anonymous:

 class foo {
    enum { bar = 1 };
 };

This effectively introduces foo as an integral constant. Note that the above is shorter than static const int.

Also, no-one could possibly write &foo if it's an enum member. If you do this:

 class foo {
    static const int bar = 1;
 }

and then the client of your class does this:

 printf("%p", &foo::bar);

then he will get a compile-time linker error that foo::bar is not defined (because, well, as an lvalue, it's not). In practice, with the Standard as it currently stands, anywhere bar is used where an integral constant expression is not required (i.e. where it is merely allowed), it requires an out-of-class definition of foo::bar. The places where such an expression is required are: enum initializers, case labels, array size in types (excepting new[]), and template arguments of integral types. Thus, using bar anywhere else technically requires a definition. See C++ Core Language Active Issue 712 for more info - there are no proposed resolutions as of yet.

In practice, most compilers these days are more lenient about this, and will let you get away with most "common sense" uses of static const int variables without requiring a definition. However, the corner cases may differ, however, so many consider it to be better to just use anonymous enum, for which everything is crystal clear, and there's no ambiguity at all.

Pavel Minaev
Hmm, gcc doesn't complain about this even with -pedantic -Wall -Wextra. I'd guess because it can't detect the error until link time anyway, it just gives up on trying to diagnose non-portable code of this kind.
Steve Jessop
Good answer. Another nice thing you don't explicitly mention is that an enum is a rvalue, and so no storage is ever allocated for it. It may be nice to know that *for sure*, rather than relying on compiler optimization if you're doing template metaprogramming and instantiating a large number of class templates.
jalf
If there's no definition for a `static const int` outside of class, there won't be any storage allocated for that either. Also, rvalues can well have storage allocated for them (temporaries are rvalues, for example, and they definitely use storage).
Pavel Minaev
Note that the wording as it is currently in the C++0x draft and which the linked defect report uses *does* allow many uses outside of where a constant expression is required. For example, if you change his example in the defect report to `return x ? +S::a : +S::b;` then the code is alright - because the lvalue to rvalue conversion is immediately applied. The main resolution to the defect against C++03 is at http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#454 and is already incorporated into the current working paper. In this sense, a proposed solution already exists :)
Johannes Schaub - litb