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_cast
ing 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
void
either as a virtual base (or static_cast
would break) or a non-virtual base (or conversions to void*
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.