Static Const Member Value VS. Member Enum:Which Method Is Better & Why

static const Member Value vs. Member enum : Which Method is Better & Why?

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.

static const vs #define vs enum

It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignoring issues about the choice of name, then:

  • If you need to pass a pointer around, you must use (1).
  • Since (2) is apparently an option, you don't need to pass pointers around.
  • Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
  • (1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
  • (1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
  • Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
  • (1) cannot be used in places like switch statements; both (2) and (3) can.
  • (1) cannot be used to initialize static variables; both (2) and (3) can.
  • (2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
  • You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.

So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.

If you were asking about C++, then you'd use option (1) — the static const — every time.

enum and static const member variable usage in template trait class

value is an enum of anonymous name in StreamInsertionExists<T>. When you try to do:

std::cout << StreamInsertionExists<T>::value;

The compiler is doing overload lookup on operator<<(std::ostream&, StreamInsertionExists<T>::E). In the typical case, it'd do integral promotion on the enum and stream it as int. However, you additionally defined this operator:

template<typename T> 
NotDefined& operator << (std::ostream&, const T&);

That is a better match for the enum than the int version (Exact Match vs integral promotion), so it is preferred. Yes, it's a function template, but non-templates are only preferred if the conversion sequences match - and in this case they don't.

Thus, this line:

std::cout << TEST::StreamInsertionExists<A>::value << std::endl;

which is:

operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl);

And the inner most operator<< call will use your NotDefined& template. Thus, the next step would be to find an appropriate function call for:

operator<<(NotDefined&, std::endl);

and there is no such overload for operator<< hence the error.

If you change value to be bool, there is no such problem because there is an operator<< that takes bool exactly: #6. That said, even with bool, your trait is still incorrect as it always returns false. Your NotDefined version actually returns a reference, so you'd have to check against that. And also, NotDefined& means not defined, so you'd have to flip the sign:

static const bool value = !std::is_same<decltype(s << t), NotDefined&>();

However, this is particularly error prone. Now cout << B{}; instead of failing to compile would instead give you a linker error, and cout << B{} << endl; gives you the same confusing overload error involving endl instead of simply statying that you can't stream a B.

You should prefer to just do:

template <typename...>
using void_t = void;

template <typename T, typename = void>
struct stream_insertion_exists : std::false_type { };

template <typename T>
struct stream_insertion_exists<T, void_t<
decltype(std::declval<std::ostream&>() << std::declval<T>())
> > : std::true_type { };

Advantage of replacing static const members with static member functions

One reason is that in this form, there is no need for defining the static member outside the class, which in cases where the class is implemented inside a header can be undesirable.

A second reason is extensibility, you could one day want your invalid ID to do some other constexpr logic in its initialization, other than calling one constexpr function like max

What's the advantage of a Java enum versus a class with public static final fields?

Technically one could indeed view enums as a class with a bunch of typed constants, and this is in fact how enum constants are implemented internally. Using an enum however gives you useful methods (Enum javadoc) that you would otherwise have to implement yourself, such as Enum.valueOf.

Should static variables be replaced with enums?

Enums are typed.

That is, if you have a method where you have to pass a certain 'state' to a method for instance, you can only pass 'valid' arguments.
For instance:

enum OrderState 
{
pending = 1,
shipped = 2
}

public IList<Order> GetOrdersInState( OrderState )
{
}

This is a good example -imho- of using enums.
When OrderState is an int for which you create 2 const ints, you have no restriction and are able to pass invalid values. The compiler won't complain.

However, the case that you're bringing up, I think using enums is not a valid solution. It's a misuse of using an int, and a const int should be used.

Enums are good, but they should be used where they must be used. They're not the preferred tool in every situation.
Having a const or static var is in this case not an antipattern.



Related Topics



Leave a reply



Submit