Quimby
Quimby

Reputation: 19223

Why is there no << overload for printing std::byte?

The following code does not compile in C++20

#include <iostream>
#include <cstddef>

int main(){ 
    std::byte b {65};
    std::cout<<"byte: "<<b<<'\n';// Missing overload
}

When std::byte was added in C++17, why was there no corresponding operator<< overloading for printing it? I can maybe understand the choice of not printing containers, but why not std::byte? It tries to act as primitive type and we even have overloads for std::string, the recent std::string_view, and perhaps the most related std::complex, and std::bitset itself can be printed.

There are also std::hex and similar modifiers, so printing 0-255 by default should not be an issue.

Was this just oversight? What about operator>>, std::bitset has it and it is not trivial at all.

EDIT: Found out even std::bitset can be printed.

Upvotes: 23

Views: 3876

Answers (2)

Ruks
Ruks

Reputation: 3956

From the paper on std::byte (P0298R3): (emphasis mine)

Design Decisions

std::byte is not an integer and not a character

The key motivation here is to make byte a distinct type – to improve program safety by leveraging the type system. This leads to the design that std::byte is not an integer type, nor a character type. It is a distinct type for accessing the bits that ultimately make up object storage.

(emphasis mine)

As such, it is not required to be implicitly convertible/interpreted to be either a char or any integral type whatsoever and hence cannot be printed using std::cout unless explicitly cast to the required type.

Furthermore, see How to use new std::byte type in places where old-style unsigned char is needed?.

Upvotes: 22

jaskij
jaskij

Reputation: 251

std::byte is intended for accessing raw data. To allow me to replace that damn uint8_t sprinkled all over the codebase with something that actually says "this is raw and unparsed", instead of something that could be misunderstood as a C string.

To underline: std::byte doesn't "try to be a primitive", it represents something even less - raw data.

That it's implemented like this is mostly a quirk of C++ and compiler implementations (layout rules for "primitive" types are much simpler than for a struct or a class).

This kind of thing is mostly found in low level code where, honestly, printing shouldn't be used. Isn't possible sometimes.

My use case, for example, is receiving raw bytes over I2C (or RS485) and parsing them into frame which is then put into a struct. Why would I want to serialize raw bytes over actual data? Data I will have access to almost immediately?

To sum up this somewhat ranty answer, providing operator overloads for std::byte to work with iostream goes against the intent of this type.

And expressing intent in code as much as possible is one of important principles in modern programming.

Upvotes: 8

Related Questions