Let us begin with an example:
#include <cstdio>
struct Base { virtual ~Base() {} virtual void foo() = 0; };
struct P: Base { virtual void foo() override { std::printf("Hello, World!"); } };
struct N: Base { virtual void foo() override {} };
void magic(Base& b);
// Example implementation that changes the dynamic type
// {
// void* s = dynamic_cast<void*>(&b);
// b.~B();
// new (s) N();
// }
int main() {
std::aligned_storage<sizeof(P), alignof(P)> storage;
void* s = static_cast<void*>(storage);
new (s) P();
Base& b = *static_cast<Base*>(s);
magic(b);
b.foo();
}
What, according to the Standard, should b.foo()
print ?
Personal opinion: it's undefined because b
got stale after we destroyed the instance in magic
. In this case, would replacing b.foo()
by static_cast<B*>(s)->foo()
make it legal ?
So now that we have an example that may (or not) be legal, the more general question at hand for all of us standardistas is whether changing the dynamic type of an object is ever allowed. We already know that the C++ compiler may reuse storage (fortunately), so it's a bit tricky.
The question might seem theoretical, however it has immediate application for compilers: may the compiler devirtualize b.foo()
to b.P::foo()
in the program above ?
And therefore I am looking for:
- a definite answer regarding my own little program (I could not come up with one).
- a possible example (a single would suffice) of a legal way of changing the dynamic type of an object.
It's undefined behaviour. Your
magic
example is violating the semantics of a reference.Also,
dynamic_cast
is for down-casting. Cast tovoid*
is astatic_cast
.To answer your questions explicitly:
You cannot change the dynamic type of an object, the closest thing you can do is re-assign a pointer.
But
p
andn
are of fixed type until they go out of scope (or, if allocated on the heap, when they aredelete
d).According to §8.5.3.2 of the standard, a reference cannot be bound to another object after initialisation. Since placement
new
creates a new object, you are violating that rule, and get undefined behaviour.The dynamic type of an object cannot change. Even in your example, you're not changing the type of an object, but creating a new different object in the same place as the old one. If you think about it, changing the dynamic type of an object would imply resizing the object in-place to accommodate extra data members and changing the VMT (and then that would move other objects and screw up pointers...) which can't be done within the rules of the language.