std::byte
operations are misspecifiedSection: 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.
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.
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.
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
-2- Effects: Equivalent to:is_integral_v<IntType>
istrue
.return b = b << shift
byte(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
-4- Effects: Equivalent to:is_integral_v<IntType>
istrue
.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
-6- Effects: Equivalent to:is_integral_v<IntType>
istrue
.return b = b >> shift
byte(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
-8- Effects: Equivalent to:is_integral_v<IntType>
istrue
.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
-17- Effects: Equivalent to:is_integral_v<IntType>
istrue
.return static_cast<IntType>
IntType(b);