>I ended up having to do some research on why the cast the
>OP mentioned was legal, and I admit I still do not understand.
Put simply, the cast is an "I know what I'm doing" switch that turns off the safety net. In this case, and on the OP's implementation, it happened to work out.
>I did forget that ( Base* ) is a C-style cast and that I
>do not understand the way the compiler interprets it.
The cast says
"Here is an address. Pretend the memory at that address represents this type"
Since Base and Derived don't have any data, there's a very good chance that the object mappings will overlay perfectly, and accessing d's virtual pointer through a pointer to Base will produce the expected behavior. But let's pretend that Base and Derived have data:
#include <iostream>
class Base {
const char *p;
public:
virtual void foo() { std::cout<<"Base\n"; }
};
class Derived: private Base {
const char *q;
virtual void foo() { std::cout<<"Derived\n"; }
};
int main()
{
Derived d;
Base *p = (Base*)&d;
p->foo();
}
For the sake of argument, assume that this is how an object of Base is represented internally:
Base::{ vptr | p }
And an object of Derived is represented like so:
Derived::{ Base::{ vptr | p } | q }
It's fair to say the cast will work, and the result will be polymorphic because taking memory originally mapped as Derived and treating it like it's Base will have the same net effect. The vptr is in the same place (but points to a different table), so accessing foo through p is safe and meaningful.
Now let's move on to a different implementation that places the base class subobject at the end of an object. Base would still look like this:
Base::{ vptr | p }
But Derived now looks like this:
Derived::{ q | Base::{ vptr | p } }
So what happens when memory originally mapped as Derived is accessed through a pointer to Base that assumes the offset of vptr is where q actually resides? q isn't a pointer to a virtual table, so any number of things could happen, but polymorphic behavior is unlikely.