user3266738
user3266738

Reputation: 497

Why am I able to cast the this pointer of a base class to a pointer to a child class using the CRTP?

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

Answers (1)

Remy Lebeau
Remy Lebeau

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

Related Questions