Reputation: 497
Consider the following classes, which employ the Curiously Recurring Template Pattern (CRTP):
template <typename T>
class Base
{
public:
virtual ~Base() {}
void typeOfThis()
{
cout << "Type of this: " << typeid(this).name() << '\n';
cout << "Type of *this: " << typeid(*this).name() << '\n';
}
void callFuncOfTemplateParam()
{
static_cast<T*>(this)->hello();
}
};
class Derived : public Base<Derived>
{
public:
void hello()
{
cout << "Hello from Derived!" << '\n';
}
};
When I execute the following:
Base<Derived> * basePtrToDerived = new Derived();
basePtrToDerived->typeOfThis();
basePtrToDerived->callFuncOfTemplateParam();
I obtain these results, which make sense to me:
Type of this: P4BaseI7DerivedE
Type of *this: 7Derived
Hello from Derived!
Clearly, the call to hello
inside of callFuncOfTemplateParam
succeeds because the this
pointer points to an instance of Derived
, which is why I am able to cast the this
pointer from the type Base<Derived>*
to the type Derived*
.
Now, my confusion arises because when I execute the following:
Base<Derived> * basePtrToBase = new Base<Derived>();
basePtrToBase->typeOfThis();
basePtrToBase->callFuncOfTemplateParam();
I obtain the following results:
Type of this: P4BaseI7DerivedE
Type of *this: 4BaseI7DerivedE
Hello from Derived!
The types of this
and *this
make sense, but I don't understand how the call to hello
succeeds. this
doesn't point to an instance of Derived
, so why am I able to cast the type of this
from Base<Derived>
to Derived
?
Note that I also replaced the call to static_cast<T*>(this)->hello();
with a call to dynamic_cast<T*>(this)->hello();
, and I still obtain the same results. I expected the dynamic_cast
to return a nullptr
, but it doesn't.
I am very surprised by these results. Thank you for any help clarifying my doubts!
Upvotes: 1
Views: 264
Reputation: 597036
The cast used to call hello()
has undefined behavior when T
does not match the true type of the object that this
points to. But hello()
isn't accessing anything via this
, so it doesn't really matter what this
actually points to. You could just as easily do reinterpret_cast<T*>(12345)->hello()
and it would still "work". However you decide to cast this
wont make any difference since hello()
simply ignores the result (in the case of dynamic_cast
, see Does calling a method on a NULL pointer which doesn't access any data ever fail?).
Change your classes to introduce data members that hello()
tries to access via this
, and you will see very different results (ie, the code will likely crash, or report garbage, etc).
Upvotes: 4