This proposal aims to improve the ergonomics of the std::byte type by allowing the conversion of an integer-literal
to std::byte.
This would simplify initialization and comparison of std::byte, which would encourage its use,
and make it feel more like a fundamental type.
C++17 has introduced std::byte as a the canonical byte type.
While it models a byte much more accurately than other types such as unsigned char, or let alone uint8_t,
it suffers from poor ergonomics.
This is largely because it has been defined as:
enum class byte : unsigned char {};Thus, std::byte is limited in its ergonomics by what is possible with scoped enumerations.
This proposal aims to make std::byte a more ergonomic type by lifting some of those imposed restrictions.
A typical use case of std::byte is the use in input/output applications.
For example, we could use it to verify the file signature of an ELF executable:
static constexpr std::array magic = {
std::byte{0x7F}, std::byte{0x45}, std::byte{0x4C}, std::byte{0x46}
};
if (std::ranges::starts_with(file_data, magic)) // if true, the file is an ELF fileThe repetition of std::byte{} is annoying and cannot be easily avoided.
We could create a variadic function to construct arrays of std::byte, but this arguably shouldn't be necessary.
Another common operation with bytes is comparison with specific values:
std::byte b = ...;
if (b == std::byte{0x00})Once again, this leads to unavoidable repetition of std::byte{} unless we introduce additional utility functios.
In summary, initialization and comparison for std::byte has poor ergonomics.
This makes the type unattractive to language users.
In my personal experience, many C++ users simply fall back to unsigned char or even std::uint8_t,
which don't suffer from these issues.
A conversion of an integer-literal to std::byte should be possible.
This makes the following code valid:
std::array<std::byte, 4> magic = { 0x7F, 0x45, 0x4C, 0x46 };
if (magic[0] == 0x7F) // trueThis would be similar to pointer conversions, where only an integer-literal with value zero can be converted to a pointer.
This proposal does not aim to make the following code valid:
constexpr int x = 0;
std::byte b = x; // still ill-formedIt would soften up the type safety which std::byte aims to achieve too much if it was possible to initialize it with
a constant expression, not just with a literal.
Any code which performs overload resolution with std::byte would be impacted:
void foo(std::byte); // new fancy std::byte overload
void foo(unsigned char); // legacy API
int main() { foo(0); }The call to foo(0) would be ambiguous because the conversion sequences int -> unsigned char and int -> std::byte have the same length.
Previously, only foo(unsigned char) would have been a candiate.
Code of this form is likely uncommon because the user of a foo function would likely already be in posession of an
unsigned char or std::byte object, rather than calling it with a literal.
The most obvious choice would be operator""b.
However, this cannot be done because it would break existing hexadecimal literal such as 0xab.
operator""y is already used by <chrono> as a year literal, and operator""t feels too far fetched.
operator""_b is not reserved for use by the standard library and would also break an established pattern of not using underscores.
In any case, the goal is to make std::byte feel more like a first-class type, and having to use user-defined literals to achieve
basic ergonomics is detrimental to that goal.
I believe that the problem isn't scoped enumerations as a whole, just std::byte.
There appears to be little demand for making the use of scoped enumerations more ergonomic in general,
but std::byte is a consistent nuisance.
Allowing the initialization of scoped enumerations using integer-literals isn't always appropriate. For example:
enum class color { red, green, blue, orange, purple };
color c = 0;This code makes no intutive sense. Of course, the restrictions could only be lifted for scoped numerations with a fixed underlying type and with no named constants. However, it is not obvious whether "enumeration conversions" would be universally appropriate in such a case.
An opt-in version of this initialization would require changes to the language syntax and is not within the scope of this proposal.
None yet.
Note: the proposed wording is heavily WIP and merely floating an idea at this point.
To subclause [conv], add a subclause [conv.byte]:
An integer-literal whose value is representible by
unsigned charcan be converted to a prvalue of typestd::byte. The value of the result is the value of the integer-literal.
Add an example:
std::byte a = 0; // OK std::byte b(0); // OK std::byte c = {0}; // OK void foo(std::byte); foo(0); // OK
To table [tab:over.ics.scs] add a row:
Conversion Category Rank Subclause Byte conversions Conversion Conversion [conv.byte]
Yep, I keep trying to use
std::bytein my projects, but I ultimately keep falling back touint8_t, including today where I just encountered: