posted:
In my compiler, the following code generates an error:
union U
{ int i; double d; };
U u;
int *ip = &u.i;
U *up = static_cast<U *>(ip); // error
I have to change the cast to reinterpret_cast for the code
to compile. It seems to me that casting from a (top-level)
union member pointer to a pointer to a union is comparably
as dangerous as casting from a base to a derived class
pointer.
There's no such thing as danger in programming. Look at it three ways:
1) Undefined Behaviour
This is crazy code that will _never_ work. Something like:
int* p;
*p = 6;
2) Well-defined Code
The code does exactly what you want it to do _all_ of the time:
int r = 4;
int* p = &r;
*p = 6;
3) Well-defined Code which may result in undefined behaviour on certain
platforms. It's the opposite to "portable code", ie. it's non-portable code.
For instance:
int r = 3;
r += 4000000;
On a lot of systems, this is okay because an "int" can hold the value... but
on other systems an "int" is too small, and so you'll have overflow on a
signed integral type, which results in Undefined Behaviour.
Now let's look at your code. This is what you're trying to do:
union PrimateUnion
{
int monkey;
double ape;
};
int main()
{
PrimateUnion a;
int* p = &a.monkey;
//Now get back a pointer to the union
PrimateUnion* p_a = p;
}
As you know, we can make this compile by using reinterpret_cast.
But the question is... what category of code does it fit into. Undefined
Behaviour? Well-defined and Portable? Well-defined and Non-portable?
The system which I'm most familiar with is Microsoft Windows. The code will
run absolutely fine on Microsoft Windows.
But what about portability? Will it run perfectly on _every_ platform? The
answer is to be found in the C++ Standard.
What you need to find out is:
When you have a union object, is the address of any element in the union
object _always_ equal to the address of the union object itself? If the
Standard provides such a guarantee, then the code is Well-defined and fully
portable. If you can find no such guarantee in the Standard, then your code
is ill-formed (even though it may run perfectly on some systems).
So if we get out the Standard and search for "union", we come up with the
following:
"Each data member is allocated as if it were the sole member
of a struct."
That doesn't tell us much... so let's look up "struct" to see what it's
getting at.
Doesn't the Standard require static cast to portably cast from base to
derived?
Yes, but the object in question must in fact be a "derived" object. You can
use "static_cast" if you're certain that it is, or "dynamic_cast" if it may
or may not be.
I believe there are no guarantees that reinterpret_cast is
ever portable.
Then it wouldn't be in the language.
Open the standard and search for "reinterpret_cast".
Since the definition of a union is that members are at
the same address, it seems like there should be some portable way to
cast a union member pointer to a pointer to the union.
Yes, we call it "reinterpret_cast". Just so you know, there are basically no
limits to what you can do in C++... just don't expect the program to work
exactly as you want on _every_ system.
Let's declare a "double" and then store a value in it as if it were an
"int":
double a;
int& b = *reinterpret_cast< int* > ( &a );
b = 5;
-Tomás