14

So I was reading the answers to dynamic_cast from "void *" and although you can't cast from a void * to a T * several of the responses point out that it is possible to cast a T * to a void *, but don't give any indication why you'd want to do that.

Is this just a bit of trivia that it's possible, or is there a case where it would make sense? I thought about maybe for readability or to make it clear that we're converting to a void *, but given the purpose of dynamic_cast, it doesn't fit very well to me.

For that matter, is there any reason to do anything other than let T * become void * implicitly? I've seen C-style casts to void * used from time to time for this purpose, I presume just to be explicit (assuming we're not doing something unusual like casting int to a pointer or something).

Community
  • 1
  • 1
FatalError
  • 52,695
  • 14
  • 99
  • 116
  • As far as I can tell, the result of using dynamic_cast() and using the implicit conversion to void * always return the same result if the source type is polymorphic. I suppose you could use it like a static_assert(is_polymophic(x)), but I can't really think of a good reason to do that. – Vaughn Cato Feb 28 '12 at 02:07

3 Answers3

11

First, when using dynamic_cast<void*>(x) you get a pointer to the first byte of the most derived object. As long as the static type of x is polymorphic.

This can be useful in a handful of scenarios, where the address serves as object identity:

  • you now have a way to fully distinguish pointers to subobjects of the same object from pointers to unrelated subobjects.
  • you can now walk some twisted graphs without visiting the same object several times... which can be used for serialization.

Granted, this is certainly not a daily usage, but in C++ the memory address is a de-facto identifier for objects, so a mechanism to access it from any part of the inheritance hierarchy certainly is useful for those few edge cases.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 2
    So, if I use a base class pointer and cast it to the derived class, the actual address value may be different? Sorry if that's a dumb question, I've just let it be magic to me for the most part ;). – FatalError Feb 28 '12 at 22:10
  • 4
    @FatalError: (note: pragmatic discussion) in the case of single inheritance, with the base class being polymorphic, the address should in fact be the same. However as soon as you start to factor multiple inheritance or if the base is not polymorphic but the derived class is, then addresses may vary. You can think of a base class as the first (hidden) attribute --> after all they are first in the initialization list of a constructor; therefore, when there are multiple classes, the beginning of the second base and the current derived do not coincide any longer (baring exceptions). – Matthieu M. Feb 29 '12 at 07:16
  • 2
    I tried it out and you were exactly right, when I tried with multiple inheritance I found the address changed with dynamic cast but not with static cast or a C-style cast. Thanks! – FatalError Mar 01 '12 at 15:13
  • Note that this also helps in calling (virtual) member functions of the object to which you may not have a derived enough pointer (especially useful in cases of multiple inheritance). – rubenvb Jun 07 '17 at 17:39
  • @rubenvb: Sorry, not following you here... how are you calling (virtual or not) member functions on a `void*`? Do you perform a `reinterpret_cast` following the `dynamic_cast`? – Matthieu M. Jun 07 '17 at 18:56
  • @MatthieuM. I'm storing pointers to a class `trackable` guaranteed by me to be in the hierarchy, unrelated to the functions called, together with a member function pointer. The call interface does go over `const void*` to be generic at that level. When I am in a situation that this `trackable` is not the first base class of a class I want to invoke a member function of, I see that I need to `dynamic_cast` the `trackable*` I stored in order to properly invoke the member function. – rubenvb Jun 07 '17 at 19:10
6

There is a purpose to this, kinda. It is hinted at in the part of the spec that allows it. From N3337, section 5.2.7, paragraph 7:

If T is “pointer to cv void,” then the result is a pointer to the most derived object pointed to by v.

So a dynamic_cast<void*>(...) is really shorthand for static_cast<void*>(dynamic_cast<MostDerivedType*>(...)). And that would be useful... sort of.

The difficulty in making it useful is that you don't necessarily know what MostDerivedType is. After all, it can be different for each expression. So once you have it as a void*, you don't necessarily have a way to cast it back safely. If you make a guess about MostDerivedType and just static_cast it, and you're wrong, then you're in undefined behavior land. Whereas if you do the dynamic_cast to that type (then static_cast to void*), it will at least return NULL if it isn't of that type.

So no, I would say that it isn't very useful. Not if you want to live within the boundaries of C++ and not rely on potentially undefined behavior.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • If you have two object pointers, you don't necessarily know whether they point to the same object — with multiple inheritance, they could point to different subobjects of the same object. To see if they're for the same object, cast both to `void*`, and compare the results. Windows COM has a similar rule: `QueryInterface` for `IUnknown`, and you'll get the same result for the same object, no matter which of that object's interfaces you start with. – Rob Kennedy Feb 28 '12 at 02:39
  • That doesn't explain why `dynamic_cast` needs to do that. `static_cast` is already said to work the same way. – Nicol Bolas Feb 28 '12 at 02:43
  • @NicolBolas: `static_cast` does not work with virtual inheritance. – Matthieu M. Feb 28 '12 at 07:35
  • @MatthieuM.: Not according to section 4.10, paragraph 2. `A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject).` Note that this is the exact same language as used for `dynamic_cast`. – Nicol Bolas Feb 28 '12 at 14:41
  • 1
    @NicolBolas: this is plain conversion, `static_cast` is more limited. See 5.2.9 [expr.static.cast] (n3337). `static_cast` is specifically limited (for pointer) to conversions that require a fixed offset (thus the name, `static`, which expresses that the offset is computed at compile-time). For example `static_cast(b)` is allowed if *B is neither a virtual base class of D nor a base class of a virtual base class of D.* – Matthieu M. Feb 28 '12 at 14:54
  • It would be more useful if you could dynamic cast it back. But it seems you cannot. :-( – André Caldas Apr 07 '23 at 12:10
1

It can be useful when implementing wrappers for operators new and delete. The thing is that operator delete allows us to deallocate memory using pointer to a base of a polymorphic object. In such situation, the wrapper would not know, which object is really being deallocated there (out of all the objects that were allocated by the wrapper before). So we use dynamic_cast< void * >() as unique identity of the object - it casts to the most derived type of a polymorphic object. This allow us to keep track of all the objects currently allocated through this wrapper. Such wrapper can be used for memory allocation tracking and for batch deallocations.

Ivorne
  • 191
  • 6