The following is a quote from the standard (draft n4861) under [bit.cast] (emphasizes are mine)
Returns: An object of type
To
. Implicitly creates objects nested within the result (6.7.2). Each bit of the value representation of the result is equal to the corresponding bit in the object representation offrom
. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object’s type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified.
So my question is, what would be an example of a scenario where std::bit_cast
produces a value representation that is corresponded to multiple different values?
Sum types come to mind. A union can have members of the same type:
union u {
int a;
int b;
};
Now, if we bit-cast something into a u
, what sort of u
do we get? Is it one where a
is active or b
? Those are formally different values of a u
, but they would seem to have the same value representation.
In the original proposal, that paragraph was written as: http://wg21.link/P0476r1
Returns: an object of type
To
whose object representation is equal to the object representation ofFrom
. If multiple object representations could represent the value representation ofFrom
, then it is unspecified whichTo
value is returned. If no value representation corresponds toTo
's object representation then the returned value is unspecified.
So it seems like the intention was that padding bits are allowed to be changed. That is, there are multiple possible object representations for the same value representation of From
with different padding, so there are multiple possible value representations in To
with different values. For example, when bit_cast
ing like this:
struct foo {
std::uint8_t a;
// 1 byte padding to align
std::uint16_t b;
};
bit_cast<std::uint32_t>(foo_value);
The padding byte is allowed to be different (so multiple bit_cast
s are allowed to return different values, but round-trip bit casting will conserve value in this case)
But there seems to be situations that break round-tripping. Consider this case (https://godbolt.org/z/KGTGes):
int a[1];
int b[1];
assert(std::begin(a) == std::end(b)); // 1
std::uintptr_t a_bits = std::bit_cast<std::uintptr_t>(std::begin(a));
std::uintptr_t b_bits = std::bit_cast<std::uintptr_t>(std::end(b));
assert(a_bits == b_bits); // 2
The first assertion is allowed to pass (And does in unoptimized builds). When it does pass, the second usually does too. When bit_cast
back to int*
, what index should be valid? [-1]
to get the value of b[0]
(if it is std::end(b)
), or 0
for a[0]
(if it is std::begin(a)
)? It seems to be unspecified
There are other cases where pointers can have the same value but be different. For an array T[N]
, casting its address, a T(*)[N]
, to void*
will have the same value as casting a pointer to the first element, a T*
to void*
. The same happens when using an empty class member with [[no_unique_address]]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With