12

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.

Marlon
  • 19,924
  • 12
  • 70
  • 101
DangerMouse
  • 704
  • 7
  • 20
  • 1
    does it make sense to have a `void**` at all? – moooeeeep Jul 10 '12 at 10:12
  • I'm coding to a C-style call-back messaging style interface and receive a pointer to a struct. The same C call is used to get back all structs so not sure I have many other options. – DangerMouse Jul 10 '12 at 10:31
  • Similar to http://stackoverflow.com/questions/11409289/c-comparison-of-array-of-pointers, although that was C and the other questioner was also asking how to work around it in their case. – Steve Jessop Jul 10 '12 at 10:59
  • @moooeeeep A `void**` is a pointer to `void*`, it might to used to "pass by reference" a `void*` to a function in C. `realloc` interface could have been defined that way. I am *not* advocating this idea here, just saying it is a possibility. – curiousguy Jul 24 '12 at 05:39
  • @DangerMouse "`static_cast`" you know this **const** has no purpose or effect, don't you? – curiousguy Jul 24 '12 at 05:41

4 Answers4

12

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • So I could do static_cast(&pFred) presumably? – DangerMouse Jul 10 '12 at 10:38
  • "void* plays the role of int (say), and Fred* the role of double." - or the other way around, since (typically) every value of `int` is representable as `double`, and (always) every value of `Fred*` is representable as `void*`. The fact that X converts to Y sometimes lulls people into thinking that X* might convert to Y*, I think. – Steve Jessop Jul 10 '12 at 11:01
  • 1
    @SteveJessop Yes. I didn't consider that aspect when I thought of the example. It was just the idea that you can convert between two "values", but you couldn't convert between two pointers to those values. – James Kanze Jul 10 '12 at 11:15
  • @DangerMouse Yes, but you can't dereference that pointer. The only legal way you can dereference it is by casting it back to a `Fred*` (modulo cv-qualifiers), and use that. – James Kanze Jul 10 '12 at 11:21
  • @JamesKanze What's the rationale of exempting conversion to void*? why doesn't that apply to void**? – Chethan Jul 10 '12 at 14:05
  • 1
    @Chethan History, really. There are still situations where it makes sense to have a pointer to something, without knowing what (although in earliest C, that was `char*`). It was also agreed that conversions of `T*` to that pointer (now `void*`) could be implicit. Later, it was decided that the reverse of any implicit conversion should be a `static_cast`. None of this applies to `void**`, of course, since `void**` doesn't point to just anything; it points to a concrete data type, `void*`. – James Kanze Jul 10 '12 at 15:22
  • @JamesKanze "_the reverse of any implicit conversion_" the reverse of implicit **pointer->pointer** conversions that do not increase cv-qualification. – curiousguy Jul 24 '12 at 06:14
  • @curiousguy Good point. Cv-qualifications are a separate issue, and you can't use `static_cast` to reduce cv-qualification (at any level). – James Kanze Jul 29 '12 at 15:24
3

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

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
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.

strnk
  • 2,013
  • 18
  • 21
  • If I get you, you're saying that a pointer to void* may be a different sort of thing to a pointer to a Fred* but that a pointer to void can point to anything? So I could uses a void* in the function signature just as easily as a void**? – DangerMouse Jul 10 '12 at 10:36
1

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.

curiousguy
  • 8,038
  • 2
  • 40
  • 58