Is this undefined behavior?
class Derived: public Base //No other bases!!!
{
//...
};
Derived d;
void *pv = &d;
Base *b = static_cast<Base*>(pv);
b->SomeMethod();
In a sensible implementation, as long as there's single inheritance, the bits of a Base and Derived pointer would match. But does the language guarantee it?
EDIT: the Real Purpose™ is to pass a pointer to a polymorphic C++ object through a C layer back to a C++ callback. The object is created as Derived but consumed in the said callback as Base. The full story is here.
Yes, what you are doing does in fact invoke undefined-behaviour.
The standard mandates that casting from T* to void* back to T* is guaranteed to be safe, the latter T* will hold the same value as the original T*.
5.29 Static cast
[expr.static.cast]13 ... A value of type pointer to object converted to "pointer to cv void" and back, possibly with different cv-qualification, shall have its original value. `.
The above behaviour guarantees that T * p = static_cast<T*> (static_cast<void*> (some_T_ptr)) will yield a value in p such that p == some_p.
In your example we are however casting from void* to a type related to the original T, but it's not the same exact T; this means that the result of the operation is unspecified.
The compiler is safe to assume that the address in the void* pointer (pv) originally points to a Base as a result it will not do any offset modifications to properly adjust to the fact that we are going from Derived* to Base*, and not Base* to Base*.
The compiler can simply copy the value of pv into b, when it really should adjust this value as it is coming from the address of a Derived.
Derived * pd = &some_derived;
void * pv = static_cast<void*> (pd);
Base * pb = static_cast<Base*> (pv); // unsafe
Regarding
“In a sensible implementation, as long as there's single inheritance, the bits of a Base and Derived pointer would match.”
Yes for simple types, but no when base is non-polymorphic and derived introduces some virtual member.
So, a conversion Derived* to void* and then directly to Base* is Undefined Behavior not just pedantically formally, but also in practice.
In passing, the special cast dynamic_cast<void*>( p ) gives you a pointer to the most derived object if the statically known type of p is pointer to polymorphic object.
This can be useful for example for use of hash tables like std::unordered_map.
Also in passing, possibly related to your Real Purpose™, the standard guarantees that a pointer to first member of a POD type can be cast to pointer to that type, and vice versa. This is in support of a C way to emulate inheritance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With