Date: Fri, 16 Jan 2026 17:34:11 +0300
On 16 Jan 2026 10:50, Jan Schultke via Std-Proposals wrote:
> Hi, there are two issues with std::bit_cast, and I'd like to open a
> discussion for how to address these.
>
> 1. There are forms of std::bit_cast that always have undefined behavior
> but are well-formed, making it equivalent to std::unreachable(). For
> example, given struct empty{} x, std::bit_cast<char8_t>(x) always
> has undefined behavior because the padding bits of empty would
> become indeterminate bits of char8_t. I'm not entirely sure how
> difficult it is to diagnose the condition for this, but it seems
> vaguely doable with reflection.
> 2. It would be useful if you could obtain a result with padding bits
> turned into zero bits. For example, converting between integers and
> floating-point types is part and parcel of implementing numerical
> functions, but std::bit_cast<__int128>(0.0L) has undefined behavior
> when long double is an 80-bit x87 floating-point type (which is
> padded to 16 bytes). It seems implementable to have a form of
> std::bit_cast that converts padding bits in the source into zero
> bits in the destination.
>
> These two issues are closely related. The padding-wipe behavior can be
> added to std::bit_cast mostly without cost to existing code because the
> padding-wipe only happens where currently, std::bit_cast maps padding
> bits in the source onto non-padding bits in the destination. However,
> cost is added when casting to e.g. an array of bytes; in that case, the
> bytes become indeterminate and the behavior is well-defined.
>
> This presents us with a few options:
>
> 1. Make std::bit_cast ill-formed in the degenerate case, and possibly
> add another std::bit_cast_zero_padding function which wipes padding.
> 2. Make std::bit_cast wipe padding, and possibly add another function
> with the current behavior so that you can cheaply cast to byte arrays.
> 3. Keep the degenerate case as is, making std::bit_cast an alternative
> spelling of std::unreachable in some cases.
>
> I think I prefer the second option, i.e. changing the behavior of
> std::bit_cast to wipe padding, and adding another
> std::bit_cast_indeterminate_padding function for the highly exotic case
> where you bit-cast to byte arrays and cannot afford the cost of having
> some of the bytes zeroed. This is spiritually similar to making
> variables erroneous by default, and offering the [[indeterminate]] opt-out.
I haven't checked, but if the bit_cast specification says it's undefined
behavior if padding bits map onto value representation bits in the
target type then I think, it should be relaxed to say that the resulting
value of those bits is unspecified. That is, you should still be able to
use bit_cast and its result, as long as you either don't use those bits
or explicitly set them to well defined values (e.g. by masking).
Then, I think, it would be useful introduce a new function that sets the
padding bits to zero, e.g.:
template< typename T >
void clear_padding(T& val) noexcept;
which basically matches __builtin_clear_padding. Then that function
could be combined with bit_cast to make its result deterministic.
The above function is useful on its own, e.g. in atomic implementation
or to be able to compare structures with padding using memcmp in users'
code.
> Hi, there are two issues with std::bit_cast, and I'd like to open a
> discussion for how to address these.
>
> 1. There are forms of std::bit_cast that always have undefined behavior
> but are well-formed, making it equivalent to std::unreachable(). For
> example, given struct empty{} x, std::bit_cast<char8_t>(x) always
> has undefined behavior because the padding bits of empty would
> become indeterminate bits of char8_t. I'm not entirely sure how
> difficult it is to diagnose the condition for this, but it seems
> vaguely doable with reflection.
> 2. It would be useful if you could obtain a result with padding bits
> turned into zero bits. For example, converting between integers and
> floating-point types is part and parcel of implementing numerical
> functions, but std::bit_cast<__int128>(0.0L) has undefined behavior
> when long double is an 80-bit x87 floating-point type (which is
> padded to 16 bytes). It seems implementable to have a form of
> std::bit_cast that converts padding bits in the source into zero
> bits in the destination.
>
> These two issues are closely related. The padding-wipe behavior can be
> added to std::bit_cast mostly without cost to existing code because the
> padding-wipe only happens where currently, std::bit_cast maps padding
> bits in the source onto non-padding bits in the destination. However,
> cost is added when casting to e.g. an array of bytes; in that case, the
> bytes become indeterminate and the behavior is well-defined.
>
> This presents us with a few options:
>
> 1. Make std::bit_cast ill-formed in the degenerate case, and possibly
> add another std::bit_cast_zero_padding function which wipes padding.
> 2. Make std::bit_cast wipe padding, and possibly add another function
> with the current behavior so that you can cheaply cast to byte arrays.
> 3. Keep the degenerate case as is, making std::bit_cast an alternative
> spelling of std::unreachable in some cases.
>
> I think I prefer the second option, i.e. changing the behavior of
> std::bit_cast to wipe padding, and adding another
> std::bit_cast_indeterminate_padding function for the highly exotic case
> where you bit-cast to byte arrays and cannot afford the cost of having
> some of the bytes zeroed. This is spiritually similar to making
> variables erroneous by default, and offering the [[indeterminate]] opt-out.
I haven't checked, but if the bit_cast specification says it's undefined
behavior if padding bits map onto value representation bits in the
target type then I think, it should be relaxed to say that the resulting
value of those bits is unspecified. That is, you should still be able to
use bit_cast and its result, as long as you either don't use those bits
or explicitly set them to well defined values (e.g. by masking).
Then, I think, it would be useful introduce a new function that sets the
padding bits to zero, e.g.:
template< typename T >
void clear_padding(T& val) noexcept;
which basically matches __builtin_clear_padding. Then that function
could be combined with bit_cast to make its result deterministic.
The above function is useful on its own, e.g. in atomic implementation
or to be able to compare structures with padding using memcmp in users'
code.
Received on 2026-01-16 14:34:15
