I've seen reinterpret_cast
used to apply incrementation to enum classes, and I'd like to know if this usage is acceptable in standard C++.
enum class Foo : int8_t
{
Bar1,
Bar2,
Bar3,
Bar4,
First = Bar1,
Last = Bar4
};
for (Foo foo = Foo::First; foo <= Foo::Last; ++reinterpret_cast<int8_t &>(foo))
{
...
}
I know casting to a reference of a base class is safe in case of trivial classes. But since enum classes are not event implicitly converted to their underlying types, I'm not sure if and how the code above would be guaranteed to work in all compilers. Any clues?
You might want to overload operator
++
for your enum if you really want to iterate its values:and use
To answer the question of whether or not the
reinterpret_cast
is allowed, it all starts with 5.2.10/1:(emphasis mine)
The reinterpretation using references is based on pointers as per 5.2.10/11:
Which transforms the question from this:
to whether this is legal:
Next stop is 5.2.10/7:
Given 3.9/9 both
int8_t
and your enumeration type are standard layout types the question now transformed into:This is where you are out of luck.
static_cast
is defined in 5.2.9 and there is nothing which makes the above legal - in fact 5.2.9/5 is a clear hint that it is illegal. The other clauses don't help:T*
->void*
->T*
whereT
must be identical (omitting cv)My conclusion from this is that your code
is not legal, its behavior is not defined by the standard.
Also note that the above mentioned 5.2.9/9 and 5.2.9/10 are responsible for making the code legal which I gave in the initial answer and which you can still find at the top.
The increment accesses the value of
foo
through an lvalue of a different type, which is undefined behaviour except in the cases listed in 3.10 [basic.lval]. Enumeration types and their underlying types are not in that list, so the code has undefined behaviour.With some compilers that support non-standard extensions you can do it via type-punning:
but this is not portable either, because accessing
intenum::i
after storing a value inintenum::e
is not allowed by the standard.But why not just use an integer and convert as needed?
This is portable and safe.
(It's still not a good idea IMHO, because there could be several enumerators with the same value, or values of the enumeration type that have no corresponding enumerator label.)
It is safe as long as it casts to the exact underlying type of the enum.
If the underlying type of the enum class changes that
++reinterpret_cast<int8_t &>(foo)
breaks silently.A safer version:
Or,