C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Fixing std::bit_cast padding bit issues

From: David Brown <david.brown_at_[hidden]>
Date: Fri, 16 Jan 2026 12:31:29 +0100
As I understand it (and I am not sure here, and happy to be corrected),
padding has unspecified values, but when you copy them with bit_cast<>
you get indeterminate bits. And then if those bits are read, you have UB.

Could it be possible to change bit_cast<> so that these copied bits
remain unspecified? I believe an aim of the way bit_cast<> is defined
is to let it work without having to copy any padding (bits or bytes),
thus the returned value could have indeterminate bits. If it is
possible to re-classify these bits as unspecified rather than
indeterminate, without loss of efficiency, then that would be an
improvement, I think. (There may be some hardware platforms that track
determinate / indeterminate status of data.)

I think some kind of "pad to zero" concept could have uses beyond this
case, and be useful in its own right. You could have :

 T padded_to_zero(const T& x)

that would return a copy of x, where the all padding bits and bytes are
set to zero. And you could have :

 pad_to_zero(T& x)

that would zero out the padding on an existing object.

The key point with these would be that you would have a consistent
underlying representation of the object, for use with things like
hashing, digital signatures, memcmp-style comparisons, etc.


But it would not solve the indeterminate bits in bit_cast<>, without
changes to the specification of bit_cast<>. padded_to_zero(x) would
still have the same padding bits and bytes, and these would still be
indeterminate after the bit_cast<>.


On 16/01/2026 09:05, Jan Schultke via Std-Proposals wrote:
> I'm not sure how this pad_to_zero type is supposed to work. For the case
> of bit-casting long double to __int128, you would essentially need to
> decompose long double into 10 non-padding bytes and 6 bytes that are
> value-initialized.
>
> That seems like an unnecessary preliminary pseudo-bit-cast before the
> actual bit-cast, and ... why wouldn't you just do it in one step?
>
> Also, none of that addresses the degenerate case where std::bit_cast is
> equivalent to std::unreachable.
>
> On Fri, 16 Jan 2026 at 09:00, Robin Savonen Söderholm via Std-Proposals
> <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>>
> wrote:
>
> I am unsure, but another idea is if we could utilise reflection to
> create a wrapper type to control padding as a fourth option, like:
> std::bit_cast<foo>(pad_to_zero(bar)),
> where pad_to_zero is something like:
> template <typename T>
> class pad_to_zero {
> /*members of bar but with explicit padding set to zero/*
> };
> These would make the bit_cast with padding-to-zero explicit, we
> would not need to change the bit cast implementation and there may
> be other places where this could be useful.
>
> Just my thoughts.
>
> Best,
> Robin
>
> On Fri, Jan 16, 2026, 08:50 Jan Schultke via Std-Proposals
> <std-proposals_at_[hidden]
> <mailto:std-proposals_at_[hidden]>> 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.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> <mailto:Std-Proposals_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals>
>
>

Received on 2026-01-16 11:31:37