static const Member Value vs. Member enum : Which Method is Better & Why?
Solution 1:
The enum
hack used to be necessary because many compilers didn't support in-place initialization of the value. Since this is no longer an issue, go for the other option. Modern compilers are also capable of optimizing this constant so that no storage space is required for it.
The only reason for not using the static const
variant is if you want to forbid taking the address of the value: you can't take an address of an enum
value while you can take the address of a constant (and this would prompt the compiler to reserve space for the value after all, but only if its address is really taken).
Additionally, the taking of the address will yield a link-time error unless the constant is explicitly defined as well. Notice that it can still be initialized at the site of declaration:
struct foo {
static int const bar = 42; // Declaration, initialization.
};
int const foo::bar; // Definition.
Solution 2:
They're not identical:
size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
Solution 3:
One difference is that the enum defines a type that can be used as a method parameter, for example, to get better type checking. Both are treated as compile time constants by the compiler, so they should generate identical code.
Solution 4:
static const
values are treated as r-values just like enum
in 99% of code you'll see. Constant r-values never have memory generated for them. The advantage enum
constants is they can't become l-values in that other 1%. The static const
values are type safe and allow for floats, c-strings, etc.
The compiler will make Foo::Life
an l-value if it has memory associated with it. The usual way to do that is to take its address. e.g. &Foo::Life;
Here is a subtle example where GCC will use the address:
int foo = rand()? Foo::Life: Foo::Everthing;
The compiler generated code uses the addresses of Life
and Everything
. Worse, this only produces a linker error about the missing addresses for Foo::Life
and Foo::Everything
. This behavior is completely standard conforming, though obviously undesirable. There are other compiler specific ways that this can happen, and all standard conforming.
Once you have a conforming c++11 compiler the correct code will be
class Foo {
public:
constexpr size_t Life = 42;
};
This is guaranteed to always be an l-value and it's type-safe, the best of both worlds.
Solution 5:
Well, if needed, you can take the address of a static const Member Value. You've have to declare a separate member variable of enum type to take the address of it.