Yes, forgot to mention that.
Note that we're talking about the standard here. Any
relationship between what the standard requires and what any
particular implementation does is purely coincidental.
I'm not convinced about that.
The note in §5.2.10/4, concerning the mapping done by
reinterpret_cast: "it is intended to be unsurprising to those
who know the addressing structure of the underlying machine."
Strictly speaking, this note concerns the mapping between
integers and pointer types, but it seems reasonable to expect it
to further apply between two pointer types.
In fact I have read experts discussing this that made it clear
that strict aliasing also applies to all types (modulo
exceptions like chars). In particular the guaranteed freedom
of aliasing between integral and floating point types could
very well speed up real numeric code (which uses integers for
indexes and floats for computation). Or at least, this is what
I've read.
It's a case of the left hand not knowing what the right hand is
doing

. The rules concerning aliasing are very important for
optimizing, and C++ very clearly does say in its object model
that accessing an object via an lvalue of a different type
(other than a character type) is undefined behavior. Regardless
of how you do it. On the other hand, reinterpret_cast is
useless unless you can do it. Clearly, reinterpret_cast is not
meant for portable code, but arguably, it should be usable in an
implementation defined manner. And the "undefined behavior" in
the object model is not because of optimizing, but because
accessing an int as a double (for example) might result in a
trapping representation. (But we don't have a rationale for the
C++ standard, so we don't know the real motivations.)
Practically, from a quality of implementation point of view, I'd
expect such accesses to behave in a manner "unsurprising to
those who know the addressing structure of the underlying
machine", and the exact representations of the types involved,
if, but only if, the reinterpret_cast is clearly visible to the
compiler, i.e. if I write something like:
void
f( double* d )
{
unsigned long long* p
= reinterpret_cast< unsigned long long* >( d ) ;
*p = 0x4000000000000000ULL ;
}
I expect the double pointed to by d to be modified to contain
the specified bit pattern, and that code calling this function,
say:
void
g()
{
double d = 0.0 ;
f( &d ) ;
std::cout << d << std::endl ;
}
will output the expected value (1.0, if I'm not mistaken with my
integral literal). Either the compiler knows what is in f()
(e.g. because it is inline), can see the reinterpret_cast, and
so knows that strict aliasing no longer applies, or it doesn't
know, in which case, it has to assume that f modifies d, and
thus reread the value before calling operator<<.
I don't expect it to work in a function which gets the two
pointers (one double*, one unsigned long long*) from some
external, unknown source.
Type aliasing is still a very controversial topic, as you can
see by browsing gcc bugzilla
It's essential for good optimization. All that one can
reasonably ask is that the compiler drop it when it sees a
reinterpret_cast.
There are also a couple of open issues on the C standard
regarding this topic.
I can believe it.
My understanding of the intent in C90 was that casting, and not
unions should be used for type punning. Admittedly, however,
this is based on somewhat uncertain memories of vague
discussions many years ago, so I'm not sure how reliable it is.
Still, it seems clear to me that a union introduces still an
additional constraint:
union U
{
double d1 ;
double d2 ;
} u ;
Formally, if you write to u.d1, and read from u.d2, you have
undefined behavior. Supposedly, in theory at least, an
implementation could keep a tag cached somewhere hidden, and
check it when you accessed. (How such an implementation would
deal with something like *(double*)(&u), I don't know, since
I think there is a guarantee that casting the address of a union
to the type of an address to one its members results in a
pointer to the member.)
Practically (again from a quality of implemenation point of
view), I'd expect type punning with a union to work as long as
the accesses are all directly to the union---again, if you take
the address of two members of different types, and pass them to
another function, I think that that function has the right to
suppose that different types means non-overlapping objects. But
I've used at least one C compiler where this was NOT the case.
[...]
The correct way to implement this type of type punning is
using std::memcpy.
The more correct thing to do is not to implement it at all

.
Of course, but in real life it is sometime necessary for some
system specific operations...
If you're writing very low level software, you almost have to.
How would you write a garbage collector without breaking typing,
for example?
... or optimizations *ducks*.
No need to duck. As I've said more than once, if the profiler
says you have to, you have to.
Practically: see the thread about reading floating point values.
You generally have an engineering decision: you don't need the
type punning, but if it can be done reasonably safely, and saves
a couple of days of development time...