I've been using std::memcpy
to circumvent strict aliasing for a long time.
For example, inspecting a float
, like this:
float f = ...;
uint32_t i;
static_assert(sizeof(f)==sizeof(i));
std::memcpy(&i, &f, sizeof(i));
// use i to extract f's sign, exponent & significand
However, this time, I've checked the standard, I haven't found anything that validates this. All I found is this:
For any object (other than a potentially-overlapping subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes ([intro.memory]) making up the object can be copied into an array of char, unsigned char, or std::byte ([cstddef.syn]).40 If the content of that array is copied back into the object, the object shall subsequently hold its original value. [ Example:
#define N sizeof(T) char buf[N]; T obj; // obj initialized to its original value std::memcpy(buf, &obj, N); // between these two calls to std::memcpy, obj might be modified std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type holds its original value
— end example ]
and this:
For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a potentially-overlapping subobject, if the underlying bytes ([intro.memory]) making up obj1 are copied into obj2,41 obj2 shall subsequently hold the same value as obj1. [ Example:
T* t1p; T* t2p; // provided that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of trivially copyable type in *t1p contains // the same value as the corresponding subobject in *t2p
— end example ]
So, std::memcpy
ing a float
to/from char[]
is allowed, and std::memcpy
ing between the same trivial types is allowed too.
Is my first example (and the linked answer) well defined? Or the correct way to inspect a float
is to std::memcpy
it into a unsigned char[]
buffer, and using shift
s and or
s to build a uint32_t
from it?
Note: looking at std::memcpy
's guarantees may not answer this question. As far as I know, I could replace std::memcpy
with a simple byte-copy loop, and the question will be the same.
f
asuint32_t
you may just write(uint32_t&)f
. It will interpret the memory location of thefloat
as if it wasuint32_t
. – Westphalint i = (uint32_t&)f;
wouldn't break the rules. – Westphalbit_cast
proposal's reference implementation usesmemcpy
andaligned_storage
. – Breakstd::vector
always contained UB (I don't know the current state though, maybe with the intruduction ofstd::launder
, it is not UB any more). – Subtilizefrexp()
, for instance and perhaps other floating-point manipulation functions in <cmath>. – Ron