1

According to std::byte's documentation on cppreference, the implementation of operator| for std::byte should be equivalent to

constexpr std::byte operator|(std::byte l, std::byte r) noexcept
{
    return std::byte{ static_cast<unsigned>(l) | static_cast<unsigned>(r) };
}

(Operators &, ^, ~ should be implemented similarly)

Why do l and r need to get cast to int unsigned if std::byte's underlying type is char unsigned?

Note: I'm aware that char unsigned{} | char unsigned{} results in an int because each operand gets promoted to int before the bitwise or is applied; while unsigned{} | unsigned{} returns an unsigned and no promotion happens. However, I don't understand which issues may such a promotion cause in this context.

paolo
  • 2,345
  • 1
  • 3
  • 17
  • note that the operands of `char unsigned{} | char unsigned{}` will be promoted to `unsigned int` in case `sizeof(unsigned char) == sizeof(int)`. They aren't always promoted to `int` – phuclv Jun 28 '22 at 16:01
  • @phuclv Thanks, I didn't know that. This makes that `static_cast` even more confusing to me. – paolo Jun 28 '22 at 16:05
  • It would be relatively unusual for `unsigned char` to have the same size as `int`; it's normally only seen on specialized hardware like DSPs that only handle word-sized operations and not bytes. But in that unusual case, the `static_cast` would just be redundant, so it doesn't really affect this question. – Nate Eldredge Jun 28 '22 at 17:27

1 Answers1

2

For integer types that are smaller then an int, they are promoted to an int before the operator is applied. That means if you had

return std::byte{ l | r };

then you would have a signed integer. By using

return std::byte{ static_cast<unsigned>(l) | static_cast<unsigned>(r) }

You explicitly convert the operands to unsigned integers so that no promotion to int happens.

This could make a difference before C++20 as signed integers were not required to use a two's complement representation unlike unsigned integer types.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    Can you provide a case where it can make a difference? – paolo Jun 28 '22 at 15:01
  • 1
    @paolo I'm not that familiar with non-twos complement representations so I don't have a concrete example I can give you. – NathanOliver Jun 28 '22 at 15:04
  • According to cppreference `std::byte` isn't a numeric type and doesn't have type casting operators, so it's unclear how those casts even work. – Mark Ransom Jun 28 '22 at 15:53
  • @MarkRansom: I was confused by that too, but I think it's meant as an informal description, to be read as if `l,r` were actually `unsigned char`. – Nate Eldredge Jun 28 '22 at 15:55
  • I was reaching the same conclusion, it's an illustration not real code. – Mark Ransom Jun 28 '22 at 15:58
  • That said, `static_cast(b)` actually does compile on all the compilers I tried, so I am not quite sure what is going on. https://godbolt.org/ – Nate Eldredge Jun 28 '22 at 15:59
  • 3
    @NateEldredge and Mark Ransom `std::byte` is an `enum class` and all enumerations can be explicitly casted to an integer type. It's just that enum class doesn't allow an implicit conversion. – NathanOliver Jun 28 '22 at 16:01
  • @paolo: At least by casting to `unsigned` you avoid having to worry at all about possible strange corner cases with signed integer representations. I would guess they chose `unsigned` here just to conform with that practice, and didn't really think about whether it was truly necessary. (I also don't know whether it is.) – Nate Eldredge Jun 28 '22 at 17:37