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?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 3
    This appears to violate the strict aliasing rule, unless `int` counts as a base class of `enum class ETest` or something – M.M Mar 15 '15 at 21:36
  • I answered a [similar question](http://stackoverflow.com/a/19477123/2073257), see if it helps you. – Daniel Frey Mar 16 '15 at 06:40

1 Answers1

1

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.

Community
  • 1
  • 1
  • 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 – Vittorio Romeo Mar 15 '15 at 22:02
  • @VittorioRomeo For serialization, you should consider `memcpy` et al. It works for *all* trivially copyable types, including enumeration types. – dyp Mar 15 '15 at 22:09
  • @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. :) –  Mar 15 '15 at 22:10
  • 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. – Ben Voigt Mar 16 '15 at 00:18
  • @BenVoigt I saw that too, but you're right that that doesn't apply: that only applies to pointer types. –  Mar 16 '15 at 08:15