c++language-lawyerc++20constexprbit-cast

Can std::bit_cast be applied to an empty object?


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?


Solution

  • 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.