I\'ve been searching for a while, but can\'t find a clear answer.
Lots of people say that using unions to type-pun is undefined and bad practice. Why is this? I can\
Unions original purpose was to save space when you want to be able to be able to represent different types, what we call a variant type see Boost.Variant as a good example of this.
The other common use is type punning the validity of this is debated but practically most compiler support it, we can see that gcc documents its support:
The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type. So, the code above works as expected.
note it says even with -fstrict-aliasing, type-punning is allowed which indicates there is an aliasing issue at play.
Pascal Cuoq has argued that defect report 283 clarified this was allowed in C. Defect report 283 added the following footnote as clarification:
If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
in C11 that would be footnote 95
.
Although in the std-discussion
mail group topic Type Punning via a Union the argument is made this is underspecified, which seems reasonable since DR 283
did not add new normative wording, just a footnote:
This is, in my opinion, an underspecified semantic quagmire in C. Consensus has not been reached between implementors and the C committee as to exactly which cases have defined behavior and which do not[...]
In C++ it is unclear whether is defined behavior or not.
This discussion also covers at least one reason why allowing type punning through a union is undesirable:
[...]the C standard's rules break the type-based alias analysis optimizations which current implementations perform.
it breaks some optimizations. The second argument against this is that using memcpy should generate identical code and is does not break optimizations and well defined behavior, for example this:
std::int64_t n;
std::memcpy(&n, &d, sizeof d);
instead of this:
union u1
{
std::int64_t n;
double d ;
} ;
u1 u ;
u.d = d ;
and we can see using godbolt this does generate identical code and the argument is made if your compiler does not generate identical code it should be considered a bug:
If this is true for your implementation, I suggest you file a bug on it. Breaking real optimizations (anything based on type-based alias analysis) in order to work around performance issues with some particular compiler seems like a bad idea to me.
The blog post Type Punning, Strict Aliasing, and Optimization also comes to a similar conclusion.
The undefined behavior mailing list discussion: Type punning to avoid copying covers a lot of the same ground and we can see how grey the territory can be.