Is it allowed to apply std::bit_cast
to an object of empty class, converting it to some not-empty class of the same size? And especially is it allowed to do in a constant expression?
I was surprised to find that the following simple program
#include <bit>
struct A {};
struct B { unsigned char b; };
// ok in MSVC and GCC, fail in Clang
static_assert( 0 == std::bit_cast<B>(A{}).b );
is accepted by both GCC and MSVC, and only Clang complains:
error: static assertion expression is not an integral constant expression
Online demo: https://gcc.godbolt.org/z/cGW3Mq3je
What is the correct behavior here according to the standard?
From [bit.cast]/2:
[...] A bit in the value representation of the result is indeterminate if it does not correspond to a bit in the value representation of from [...]. 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 ordinary character type or std::byte type. [...]
So, in your case, the behavior of the expression is undefined, but not because of the std::bit_cast
itself.
Because the bits of b
do not correspond to any bit of the value representation of A{}
, they are indeterminate and the value of b
itself is consequently indeterminate. Only because b
has type unsigned char
, this is not immediately undefined behavior.
However, for the comparison with 0 ==
, you then read the value of b
, which causes undefined behavior because its value is indeterminate.
Of course, because you are using the comparison as the condition in a static_assert
, the program will not have undefined behavior, but will be ill-formed instead, because the expression is not a constant expression. In a constant expression any lvalue-to-rvalue conversion on an indeterminate value is disallowed. The compiler needs to diagnose this and therefore only Clang is conforming.