You cannot really type-pun a bool
into an unsigned long long
via memcpy
(unless they have the same size). Putting alignas(void*)
on a bool
doesn't solve this, because you're reading bytes that are past its object representation. It is memory-safe to do because of padding in the surrounding struct
, but it's still undefined behavior in C++.
See also: Why does std::memcpy (as an alternative to type-punning) not cause undefined behaviour? tl; dr: the only thing you can do with std::memcpy
is copy values between two objects of the same type. Anything beyond that is a compiler extension.
On a side note, it would be much better to use std::uintptr_t
instead of unsigned long long
, because it is guaranteed to have the same size as void*
.
Solution with std::bit_cast
Since C++20, there is a function that does this memcpy
step for you:
auto getFailed() {
return std::bit_cast<std::uintptr_t>(this->failed);
}
However, you cannot use it, because it requires sizeof(this->failed) == sizeof(std::uintptr_t)
.
You would have to put your bool
into a wrapper struct
with the same size as unsigned long long
instead of padding it via alignas
:
struct {
// important: Don't use alignas directly, because void* could have no alignment
// requirements despite being 8 bytes in size.
// In that case, we would have no padding.
alignas(sizeof(void*)) bool value;
} failed;
However, this is still technically UB, because bit-casting padding bits is UB in most cases. You must bit-cast bits which are part of the value representation of an object, so we must add manual paddding:
alignas(void*) struct {
bool value;
char padding[sizeof(void*) - sizeof(bool)];
} failed;
Solution with reinterpret_cast
auto getFailed() {
// your code, but C-style cast converted to reinterpret_cast
return reinterpret_cast<BaseA*>(&this->failed)->failed;
}
Don't even consider this, it's undefined behavior.
It would be a violation of strict aliasing to read a std::uintptr_t
through a pointer that is actually pointing to bool
. These two types cannot alias each other.