1

Is this legal C++ (>=14), resulting in a char being read and saved into aCode?

enum class ECode : char { Code1 = 'a' };
std::istream& operator>>(std::istream& aIn, ECode& aCode)
{
  return aIn >> (std::underlying_type_t<ECode>&)aCode;
}

I would prefer return aIn >> static_cast<std::underlying_type_t<ECode>&>(aCode) which is not legal, it seems ("cannot cast to reference of unrelated type".)

However, this very similar line is legal, and is what my C-style cast should be identical to:

return aIn >> *static_cast<char*>(static_cast<void*>(&aCode))
Felix Dombek
  • 13,664
  • 17
  • 79
  • 131
  • [Duplicate](https://stackoverflow.com/q/29066335/3233393)? – Quentin Jun 10 '20 at 22:09
  • @Quentin Interesting. As the 5-year-old answer there is "this is not clear from the standard", I would still like to get answers for newer C++ versions. I'll remove the C++14 tag. – Felix Dombek Jun 10 '20 at 22:11
  • I recommend putting a bounty on the existing question, because I don't think anything's changed in this regard (and I'd frown at this code anyway tbh - why not extract into a `char`, then proper cast to an `ECode` and finally assign? it'll be super quick) – Asteroids With Wings Jun 10 '20 at 22:17
  • 4
    One way this question differs from the other one is that accessing an object via type `char` never violates the strict aliasing rule. But I don't see any guarantees relating value representations of an enumeration type and its underlying type. – aschepler Jun 10 '20 at 22:23
  • @AsteroidsWithWings That is exactly the code I have, but this still sparks the question. I like (to at least know of legal) one-liners, if only for the jest of it, even if they don't pass a code review ;) – Felix Dombek Jun 10 '20 at 22:31
  • You probably should add the `language-lawyer` tag, so you can get chapter-and-verse from the standard cited. – Eljay Jun 10 '20 at 23:16

1 Answers1

2

As has been noted in comments, there is no strict aliasing violation because char can alias any type.

In practice I doubt any real compiler would do anything other than the "obvious" implementation, i.e. give the enum the same size and representation as the underlying type. In which case your reinterpret_cast would be well-defined and behave as expected.

However the Standard (as of C++17) does not appear to guarantee that.

As far as I can see, it only specifies that any value of the underlying type can be stored in the enum object, and that static_cast can be used losslessly for values in the range of the underlying type.

There is a language lawyer question here about whether sizeof(ECode) == sizeof(char) must hold, although the answers seem to say "the standard doesn't actually say so but they probably meant it".

But even if the size is the same there isn't a representation guarantee, e.g. the bits could be stored in some different order and the static_cast transforms the bits.

In [basic.fundamental] where it specifies the representation of integer types, it even has an explicit footnote saying that enumerations are not integer types.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • The C++17 definition of [enum class byte : unsigned char {} ;](https://en.cppreference.com/w/cpp/types/byte) seems to imply that both size and representation must be identical to `unsigned char`. I wonder if this is generalizable, but it probably isn't (are they stretching the standard here? :) ). I also wonder why they don't just write what they mean ... – Felix Dombek Jun 15 '20 at 14:07
  • 1
    @FelixDombek it seems there's an oversight somewhere (not uncommon in the standard) , e.g. perhaps whoever wrote the `std::byte` addon didn't realize the underspecification of the enum size – M.M Jun 15 '20 at 20:42