I'm trying to get a bitmask from a bitfield struct, at compile time. One of the tricks that I tried, which looks promising to me, is using std::bit_cast, because it is supposed to be constexpr.
My test code can be found here: https://godbolt.org/z/er48M63sh
This is the source:
#include <bit>
struct Bitfield {
int :3;
int x:3;
};
constexpr int mask() noexcept {
Bitfield bf{};
bf.x -= 1;
return std::bit_cast<int>(bf);
}
int test() {
int mask1 = mask();
// constinit static int mask2 = mask(); // Why doesn't this compile?
return 0;
}
As you can see, it doesn't actually calculate the bitmask at compile time, but at runtime, so for some reason the constexpr trick isn't working. I fail to see why, however, since cppreference doesn't seem to list my case as one that defeats constexpr in std::bit_cast.
Does anybody see what's wrong? Does this thing have a chance of working?
The problem is that using
std::bit_castwith padding bits is undefined behavior in most cases:Most of the upper (or lower) bits in the
intthat results fromstd::bit_cast<int>(bf)are indeterminate, because it they don't correspond to bits in the value representation of the bit-field. TheBitfieldclass has plenty of padding bits, i.e. which are only in the object representation, not the value representation ofBitfield.intis not astd::byte, so it isn't exempt, and producing anintthroughstd::bit_castwhich has some indeterminate bits is undefined behavior. You only notice that it's UB in a constant expression because compilers aren't required to diagnose UB outside constant expressions.Possible Solutions
std::bit_cast, and manually serialize instead withbf.first | bf.second << 3, assuming you've given all the bit-field members a namestructstatic_assert(std::has_unique_object_representations_v<Bitfield>)