Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What would be an example where std::bit_cast produces a value representation of multiple values?

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 of from. 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?

like image 569
ph3rin Avatar asked Dec 14 '22 07:12

ph3rin


2 Answers

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.

like image 79
StoryTeller - Unslander Monica Avatar answered May 10 '23 23:05

StoryTeller - Unslander Monica


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 of From. If multiple object representations could represent the value representation of From, then it is unspecified which To value is returned. If no value representation corresponds to To'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_casting 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_casts 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]].

like image 41
Artyer Avatar answered May 10 '23 23:05

Artyer