30

C++17 introduces the std::byte type. A library type that can (supposedly) be used to access raw memory, but stands separate from the character types and represents a mere lump of bits.

So far so good. But the definition has me slightly worried. As given in [cstddef.syn]:

enum class byte : unsigned char {};

I have seen two answers on SO which seem to imply different things about the robustness of the above. This answer argues (without reference) that an enumeration with an underlying type has the same size and alignment requirements as said type. Intuitively this seems correct, since specifying an underlying type allows for opaque enum declarations.

However, this answer argues that the standard only guarantees that two enumerations with the same underlying type are layout compatible, and no more.

When reading [dcl.enum] I couldn't help but notice that indeed, the underlying type is only used to specify the range of the enumerators. There is no mention of size or alignment requirements.

What am I missing?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • The underlying type is the type used for the named constants introduced by the enumeration. In this case you have none of them, therefore all what you can do is get it out and use it directly. The good part is that it is not an alias of `unsigned char`, so you can declare functions that accepts `std::byte` while rejects plain `unsigned char`. If you go on using the underlying type through `std::underlying_type_t`, the actual type used for byte could even change (I don't see reasons for that, but who knows in future?) and your code will be generic the way it won't be affected. – skypjack Jul 17 '17 at 05:52
  • @skypjack - [dcl.enum] specifies that that it's the type of the constants only until the closing brace of the definition. After that they are of the "enumeration type". [I kid you not](http://eel.is/c++draft/dcl.enum#5). – StoryTeller - Unslander Monica Jul 17 '17 at 05:54
  • In other terms, I don't think you should concentrate on the features of an enum when looking at `std::byte`. The enum is there mainly to define a dedicated type instead of an alias. From that point on, all what interests is the actual underlying type and the fact that `auto` will let you write generic code that treats correctly a byte. – skypjack Jul 17 '17 at 05:55
  • @skypjack - I know **why** it's there. That's not the subject of the question. – StoryTeller - Unslander Monica Jul 17 '17 at 05:55
  • Yeah, right. Named constants type isn't strictly the underlying one, that's the one to which you can safely cast them. That being said, for there are no named constants there, I guess all of this is pointless at the end of the day. :-) – skypjack Jul 17 '17 at 05:57
  • Implementations are allowed to recognize the name "std::byte" and do magic beyond what the declaration seems to imply. – Marc Glisse Jul 17 '17 at 06:02
  • @MarcGlisse - That's nice and all, but would be very sad if an implementation had to do magic for such a simple type. This feels like something the core language should address. But perhaps my feelings are wrong :) – StoryTeller - Unslander Monica Jul 17 '17 at 06:04
  • 1
    Anyway I guess all your doubts are wiped out by the fact that you can use `std::byte{x}` to create a byte out of a number and `std::to_integer` for the opposite conversion. Well, doubts are still there probably, but at least you have _tools_ to work easily with bytes when you want. Get the integer, use it directly and convert it back to a byte when you are done. Pretty good indeed. ;-) – skypjack Jul 17 '17 at 06:13
  • @skypjack - If the whole purpose was creating variables of `byte` type this wouldn't be a problem. This is described as a tool to *access raw memory*. Unless I'm being too liberal with what it can used for, the grounds for it seem quite flimsy. – StoryTeller - Unslander Monica Jul 17 '17 at 06:20
  • 1
    The funniest part is that in [a first draft](http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0298r0.pdf) this word changing was proposed - _The type byte is an implementation-defined distinct type with the same size, signedness and alignment as unsigned char, called the underlying type_. This would have contained the answer to your question, but [it is has been removed](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0298r1.pdf) consequently. Damnit!! Anyway it makes me think that it was redundant and the standard probably contains those information somewhere. – skypjack Jul 17 '17 at 06:29
  • 1
    Is the following not clear about it? 6.9 Types 2/ "For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes (4.4) making up the object can be copied into an array of char, unsigned char, or **std::byte** (21.2.1).43 If the content of that array is copied back into the object, the object shall subsequently hold its original value." – Jean-Baptiste Yunès Jul 17 '17 at 07:31
  • @Jean-BaptisteYunès - The question is not about that being allowed. It's about whether the size of the *array of unsigned char* and *array of std::byte* is required to be the same. `sizeof(unsigned char) == 1`, but what about `sizeof(std::byte)`? And if you say yes, please back it with the standard. – StoryTeller - Unslander Monica Jul 17 '17 at 07:35
  • 2
    Oddly enough, [N2213](http://wg21.link/n2213) had wording that guarantees identical representation to underlying type, but that wording was removed in [N2347](http://wg21.link/n2347). In fact, it even removed the C++03 wording providing for identical `sizeof` without any obvious replacement. – T.C. Jul 17 '17 at 07:37
  • Wouldn't **Object Model** "If a complete object is created (8.3.4) in storage associated with another object e of type “array of N unsigned char” or of type “array of N std::byte” (21.2.1)" be more clear about it ? – Jean-Baptiste Yunès Jul 17 '17 at 07:50
  • 6
    The more general question regarding enums and their underlying types is probably worth a core issue, given that CWG approved this formulation of `std::byte` and presumably thought that the size/alignment relationship exists. As a practical matter, the clear intent is for `std::byte` to take up, well, one byte; no sane implementer would do it differently. – T.C. Jul 17 '17 at 07:57
  • 1
    @T.C. - Thank you. Glad to know there wasn't something obvious I was missing. I also think your aggregated comments actually form an answer to my question. Would you post them as such? – StoryTeller - Unslander Monica Jul 17 '17 at 08:17

2 Answers2

11

Essentially there is special wording all around the c++17 draft standard that gives std::byte the same properties with regard to aliasing as char and unsigned char.

To give you an example, the the C++17 standard, [basic.lval] p8 states:

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.

  • [...]
  • a char, unsigned char, or std::byte type.

Essentially, anywhere that char gets special treatment in the standard, the same is given to std::byte. As far as accessing memory is concerned, it seems irrelevant that it is defined as an enum class or what it's underlying type is.

However, enumeration types used to be under-specified until CWG 2590. Underlying type should determine size and alignment requirements of an enum, which added the wording:

An enumeration has the same size, value representation, and alignment requirements as its underlying type. Furthermore, each value of an enumeration has the same representation as the corresponding value of the underlying type.

- [dcl.enum] p9

You can assume that this defect report applies to C++17 as well.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
MikeMB
  • 20,029
  • 9
  • 57
  • 102
6

(Documenting the comments made by @T.C. that ultimately answer my question)
(I will remove this if T.C. ever wishes to reformulate his own answer.)


Oddly enough, N2213 had wording that guarantees identical representation to underlying type, but that wording was removed in N2347. In fact, it even removed the C++03 wording providing for identical sizeof without any obvious replacement.


The more general question regarding enums and their underlying types is probably worth a core issue, given that CWG approved this formulation of std::byte and presumably thought that the size/alignment relationship exists. As a practical matter, the clear intent is for std::byte to take up, well, one byte; no sane implementer would do it differently.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458