This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 2950. `std::byte` operations are misspecified

Section: 17.2.5 [support.types.byteops] Status: C++20 Submitter: Thomas Köppe Opened: 2017-03-24 Last modified: 2021-02-25

Priority: 1

View all issues with C++20 status.

Discussion:

The operations for `std::byte` (17.2.5 [support.types.byteops]) are currently specified to have undefined behaviour in general cases, since the type of the expression expr that appears in `return byte(expr)` is obtained by the arithmetic conversion rules and has higher conversion rank than `unsigned char`. Therefore, the value of the expression may be outside the range of the enum (for example, consider `~0`), and by 7.6.1.9 [expr.static.cast] p10 the conversion results in undefined behaviour.

I believe the original intent of the specification could be expressed correctly with the following, more verbose sequence of casts. I will only give one representative example:

>     byte operator<<(byte b, IntType shift)

> > Equivalent to: `return byte(static_cast<unsigned char>(static_cast<unsigned char>(b) << shift));`

[ 2017-06-27 P1 after 5 positive votes on c++std-lib. ]

[2017-06-28, STL comments and provides wording]

This proposed resolution performs its work in `unsigned int`, which is immune to promotion. For `op=`, I'm avoiding unnecessary verbosity.

It stylistically uses `static_cast`s instead of functional-style casts. All of the `static_cast`s are intentional, although not all of them are strictly necessary. I felt that it was simpler to always follow the same pattern for type conversions, instead of skipping `static_cast`s by taking advantage of the possible ranges of values. (I could prepare an alternative PR to avoid unnecessary casts.) I'm not `static_cast`ing the shift arguments, because of how 7.6.7 [expr.shift] works.

For `to_integer()`, there's a tiny question. According to 7.6.1.9 [expr.static.cast]/9, `static_cast`ing from `[128, 255]` bytes to `signed` (behavior) `char`s triggers unspecified behavior, whereas using `unsigned char` as an intermediate type would produce implementation-defined behavior, 7.3.9 [conv.integral]/3. This question is basically theoretical, and it's unaffected by this proposed resolution (which is just changing a functional-style cast to a `static_cast`).

[2016-07, Toronto Thursday night issues processing]

Status to Ready

Proposed resolution:

This wording is relative to N4659.

1. Edit 17.2.5 [support.types.byteops] as indicated:

   >     template <class IntType>
   >       constexpr byte& operator<<=(byte& b, IntType shift) noexcept;

   > > -1- Remarks: This function shall not participate in overload resolution unless `is_integral_v<IntType>` is `true`.

   > > -2- Effects: Equivalent to: `return b = b << shiftbyte(static_cast<unsigned char>(b) << shift);`

   >     template <class IntType>
   >       constexpr byte operator<<(byte b, IntType shift) noexcept;

   > > -3- Remarks: This function shall not participate in overload resolution unless `is_integral_v<IntType>` is `true`.

   > > -4- Effects: Equivalent to: `return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(b) << shift))byte(static_cast<unsigned char>(b) << shift);`

   >     template <class IntType>
   >       constexpr byte& operator>>=(byte& b, IntType shift) noexcept;

   > > -5- Remarks: This function shall not participate in overload resolution unless `is_integral_v<IntType>` is `true`.

   > > -6- Effects: Equivalent to: `return b = b >> shiftbyte(static_cast<unsigned char>(b) >> shift);`

   >     template <class IntType>
   >       constexpr byte operator>>(byte b, IntType shift) noexcept;

   > > -7- Remarks: This function shall not participate in overload resolution unless `is_integral_v<IntType>` is `true`.

   > > -8- Effects: Equivalent to: `return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(b) >> shift))byte(static_cast<unsigned char>(b) >> shift);`

   >     constexpr byte& operator|=(byte& l, byte r) noexcept;

   > > -9- Effects: Equivalent to:

   > > >     return l = l | rbyte(static_cast<unsigned char>(l) | static_cast<unsigned char>(r));

   >     constexpr byte operator|(byte l, byte r) noexcept;

   > > -10- Effects: Equivalent to:

   > > >     return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(l) |
   > > >     static_cast<unsigned int>(r)))byte(static_cast<unsigned char>(l) |
   > > >     static_cast<unsigned char>(r));

   >     constexpr byte& operator&=(byte& l, byte r) noexcept;

   > > -11- Effects: Equivalent to:

   > > >     return l = l & rbyte(static_cast<unsigned char>(l) & static_cast<unsigned char>(r));

   >     constexpr byte operator&(byte l, byte r) noexcept;

   > > -12- Effects: Equivalent to:

   > > >     return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(l) &
   > > >     static_cast<unsigned int>(r)))byte(static_cast<unsigned char>(l) &
   > > >     static_cast<unsigned char>(r));

   >     constexpr byte& operator^=(byte& l, byte r) noexcept;

   > > -13- Effects: Equivalent to:

   > > >     return l = l ^ rbyte(static_cast<unsigned char>(l) ^ static_cast<unsigned char>(r));

   >     constexpr byte operator^(byte l, byte r) noexcept;

   > > -14- Effects: Equivalent to:

   > > >     return static_cast<byte>(static_cast<unsigned char>(static_cast<unsigned int>(l) ^
   > > >     static_cast<unsigned int>(r)))byte(static_cast<unsigned char>(l) ^
   > > >     static_cast<unsigned char>(r));

   >     constexpr byte operator~(byte b) noexcept;

   > > -15- Effects: Equivalent to: `return static_cast<byte>(static_cast<unsigned char>(~static_cast<unsigned int>(b)))byte(~static_cast<unsigned char>(b));`

   >     template <class IntType>
   >       constexpr IntType to_integer(byte b) noexcept;

   > > -16- Remarks: This function shall not participate in overload resolution unless `is_integral_v<IntType>` is `true`.

   > > -17- Effects: Equivalent to: `return static_cast<IntType>IntType(b);`