Why do I need a reinterpret_cast to convert Fred *

2019-04-19 14:54发布

问题:

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.

回答1:

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.



回答2:

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*.)



回答3:

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.



回答4:

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.