While rethinking on the question I just realized of another problem you have. Polymorphic behavior can only be achieved through pointers and/or references. Trying to do so with values produces what is called slicing, where the original derived object is converted (copied) into a base object loosing all the extra information.
The common example of slicing is this one:
class Base
{
public:
virtual void polymorphic() {
std::cout << "Base" << std::endl;
}
};
class Derived : public Base
{
public:
virtual void polymorphic() {
std::cout << "Derived" << std::endl;
}
};
void f( Base b ) {
b.polymorphic();
}
int main() {
Derived d;
f( d ); // prints Base
}
Now, the original question deals with conversion of pointer types and I answered before. I believe that your code presents a variant of the slicing problem. We can play a little with the undefined behavior of forcing pointer conversions from unrelated types:
// same Base and Derived definitions as above
template <typename T>
struct Holder
{
T data;
};
int main() {
Holder<Derived> derived;
Holder<Derived>* derived_ptr = &derived;
Holder<Base>* base_ptr = reinterpret_cast<Holder<Base>* >( derived_ptr ); // undefined!!!
std::cout << "derived_ptr=" << derived_ptr << ", base_ptr=" << base_ptr << std::endl;
base_ptr->data.polymorphic(); // prints Base
}
First of all, this is undefined behavior according to the standard... but bear with me. We can try to work what a common compiler could implement. We have an object of type Holder, and a pointer to it (*derived_ptr*), now we reinterpret the pointer as a pointer to Holder. At this point *base_ptr* and *derived_ptr* have the same value, they point to the same address (check the output).
In the last step we access the data field of the Holder stored at &derived, and we call the virtual function * polymorphic()* on it... but in Holder, the data field is not a reference or pointer, it is an element of type Base, so the compiler can decide that it will call the Base version of it, and it so does in g++ 4.0.
This is all undefined behavior, so try it with your compiler of choice, it may end up with the same or different outcomes.
If you change the template definition to store a pointer, then you get the expected polymorphic behavior. That is, expected in this case, knowing the memory footprint of the objects at hand. Not that you can expect anything from the unexpected undefined behavior.