I have a const pointer to a pointer to a Fred and I don't understand why a static_cast isn't sufficient.
typedef struct {
int n;
} Fred;
Fred *pFred;
Fred **const ppFred = &pFred;
void **const ppVoid = static_cast<void ** const>(ppFred);
Please could someone explain why a reinterpret_cast is needed to convert a pointer to Fred*to a pointer to void* but static_cast is fine to convert pointer to Fred to a pointer to void.
There's no requirement that a Fred* and a void* have the same size
and representation. (I've worked on machines where they didn't,
although that was before my C++ days.) When you convert Fred* to
void*, you get a new pointer, with a possibly different size and
representation, but there is no information about the size and
representation of the object the void* points to. You know that it is
unknown, and the only way to use this void* is to cast it back to a
Fred* (modulo things like cv-qualifiers). When you convert Fred**
to void**, you're converting from a pointer to a concrete type (a
pointer to a Fred*) to a pointer to another concrete type (a pointer
to a void*). And since there's no guarantee that these two concrete
types have the same size and representation, the conversion requires a
reinterpret_cast. void is a special, non-concrete type, so you can
static_cast a pointer to any type to and from a pointer to void.
void* is just another concrete pointer type, so casting to and from
pointers to it follows the usual rules (and requires a
reinterpret_cast).
In many ways, the situation is very much like int and double, where
void* plays the role of int (say), and Fred* the role of double.
There's no problem static_casting between int and double, but
casts between int* and double* require reinterpret_cast.
All object pointers are convertible to void*, so a static cast is fine for that. However, converting between T* and U* in general requires a reinterpret cast, since arbitrary pointers are not mutually convertible. (And substitute T = Fred*, U = void*.)
static_cast won't work to convert Fred ** to void ** because it's not a sensible conversion : the pointers to Fred* and to void* are not necessarily created the same way (i.e. alignments problems on some platforms). You can be sure that a void* which can point to any byte in memory can point to a Fred object as well, but that's not the case for void** pointers.
Disclaimer
The following is hand-waving for the purpose of making things easily understood, not a technically correct description.
The hand-waving
One possible way to introduce void is:
void is similar (not the same thing as) the Java Object universal superclass.
void can be seen as an abstract base of every class and non-class type. (With this metaphor, void would also be a quasi-virtual base type: conversion to void* is never ambiguous.)
So you can see the implicit conversion from T* to void* as a derived-to-base conversion, and the reverse static_cast is like a base to derived down-cast. When a void* does not really point to a T, you should not do a static_cast<T*> (when a Base* does not really point to a Derived, you should not do a static_cast<Derived*>).
Disclaimer, again
Seriously, void is not an abstract base class, and cannot be formally treated as one in many cases:
- You cannot formally describe
voideither as a virtual base (orstatic_castwould break) or a non-virtual base (or conversions tovoid*would be ambiguous when multiple inheritance is used). - There is no
void&type. This base class metaphor just does extend beyond pointers.
Please, DO NOT go tell people "void is the universal C++ base class, like Java Object". Do not repeat anything I wrote here without the full disclaimers.
Only in some cases, void behaves like a base class for the purpose of pointer implicit conversions and casts.
You cannot write programs based on metaphors, but on the real C++ rules.
This metaphor might help. Or not. Either way, do not ever try to draw logical conclusions based on a metaphor.
来源:https://stackoverflow.com/questions/11410783/why-do-i-need-a-reinterpret-cast-to-convert-fred-const-to-void-const