2
#include <iostream>
#include <cassert>
#include <type_traits>

template<typename T> using Underlying = std::underlying_type_t<T>;

enum class ETest : int
{
    Zero = 0,
    One = 1,
    Two = 2
};

template<typename T> auto& castEnum(T& mX) noexcept
{
    // `static_cast` does not compile
    // return static_cast<Underlying<T>&>(mX);

    return reinterpret_cast<Underlying<T>&>(mX);
}

int main()
{
    auto x(ETest::Zero);
    castEnum(x) = 1;
    assert(x == ETest::One);

    return 0;
}

ideone

Is this code guaranteed to always work? Or is it undefined behavior?

2
  • 3
    This appears to violate the strict aliasing rule, unless int counts as a base class of enum class ETest or something Commented Mar 15, 2015 at 21:36
  • I answered a similar question, see if it helps you. Commented Mar 16, 2015 at 6:40

1 Answer 1

2

The standard is a bit unclear:

3.10 Lvalues and rvalues [basic.lval]

10 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

[...]

(10.4) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object,

[...]

This could be legitimately read as saying that the signed or unsigned type corresponding to an enumeration type is its underlying type, but I'd think this is meant to cover only accessing integer types through their other-signed corresponding type, that the underlying type of an enumeration type does not count as the (un)signed type corresponding to that enumeration type.

At least GCC agrees with this: it gives an aliasing warning for

enum E : int { };
int f(E e) { return *(int *) &e; }
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

strongly hinting that it will optimise on the assumption that no such aliasing takes place in your program.

Sign up to request clarification or add additional context in comments.

5 Comments

So, according to the warning gcc gives us, can we conclude that enumCast is actually unsafe and shouldn't be used? It's a shame, it's pretty convenient for defining serialization helper functions :P
@VittorioRomeo For serialization, you should consider memcpy et al. It works for all trivially copyable types, including enumeration types.
@VittorioRomeo It's hypothetically possible that GCC's optimiser and GCC's warning logic are out of sync, that the optimiser will take such aliasing into account, but I wouldn't rely on that without a very strong proof. :)
The bullet " a type similar (as defined in 4.4) to the dynamic type of the object" looks like potential permission, but the definition of similar types doesn't appear to include enums vis-a-vis underlying type.
@BenVoigt I saw that too, but you're right that that doesn't apply: that only applies to pointer types.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.