I want to know how to use std::bit_cast
in a well-defined way, noticeably in presence of indetermined bits. When is std::bit_cast
usage defined behavior, when is it undefined behavior?
Thus I need a clarification about the wording of cppreference about std::bit_cast. I don't understand the meaning of the following paragraph:
For each bit in the value representation of the result that is indeterminate, the smallest object containing that bit has an indeterminate value; the behavior is undefined unless that object is of unsigned char or std::byte type. The result does not otherwise contain any indeterminate values.
When the input (From
) is containing indeterminate bits (noticeably padding bits) what are the mentioned objects and what is meaning the distinction about their type (std::byte
vs other)?
Let's write an example (not an actual use-case, merely an illustration of a situation with indeterminate bits):
#include <bit>
#include <cstdint>
struct S40 {
std::uint8_t a = 0x51;
std::uint32_t b = 0xa353c0f1;
};
struct S3 {
std::uint8_t a : 3;
};
int main() {
S40 s40;
std::uint64_t a64 = std::bit_cast<std::uint64_t>(s40);
// (3) on tested compilers, a64 is 0xa353c0f1UUUUUU51 where U is undefined byte
S3 s3;
s3.a = 0b101;
std::uint8_t a8 = std::bit_cast<std::uint8_t>(s3);
// (4) on tested compilers, a8 is uuuuu101, u being indeterminate bits
}
Live
In this example, I'm playing with alignment in order to induce padding bits between a
and b
.
(note how clang
is actually throwing garbage into the padding bits).
Is the production of a64
undefined behavior?
with S3
, I tried to emulate a less than 1 byte type with padding bits.
Is the production of a8
defined behavior, while its value is undefined due to the presence of indeterminate bits?
For the record, I leave here the original example that tried to stress the possible difference between the 1 byte case and the other situations. But as it was rightly stressed in one answer, it does not demonstrate anything as passing a bit field inside the std::bit_cast
implied an cast to a fully defined value (of its underlying type).
#include <bit>
#include <cstdint>
struct S9 {
std::uint16_t a : 9;
};
struct S7 {
std::uint8_t a : 7;
};
int main() {
S9 s9;
s9.a = 42;
std::uint16_t a16 = std::bit_cast<std::uint16_t>(s9.a);
// (1) a16 may be uuuuuuu000101010, u being indeterminate bits
S7 s7;
s7.a = 42;
std::uint8_t a8 = std::bit_cast<std::uint8_t>(s7.a);
// (2) a8 may be u0101010, u being indeterminate bits
}
What are the objects we are speaking of?
Is the production of a8
considered as defined behavior, while its value is undefined?
Is the production of a16
considered as undefined behavior, while its value is undefined?