Почему я могу привести этот указатель базового класса к указателю на дочерний класс, используя CRTP? - PullRequest
0 голосов
/ 12 ноября 2018

Рассмотрим следующие классы, в которых используется шаблон любопытного повторения шаблона (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';
    }
};

Когда я выполняю следующее:

Base<Derived> * basePtrToDerived = new Derived();
basePtrToDerived->typeOfThis();
basePtrToDerived->callFuncOfTemplateParam();

Я получаю следующие результаты, которые имеют для меня смысл:

Type of this:  P4BaseI7DerivedE
Type of *this: 7Derived
Hello from Derived!

Очевидно, что вызов hello внутри callFuncOfTemplateParam завершается успешно, потому что указатель this указывает на экземпляр Derived, поэтому я могу привести указатель this из типа Base<Derived>* к типу Derived*.

Теперь у меня возникает путаница, потому что когда я выполняю следующее:

Base<Derived> * basePtrToBase = new Base<Derived>();
basePtrToBase->typeOfThis();
basePtrToBase->callFuncOfTemplateParam();

Я получаю следующие результаты:

Type of this:  P4BaseI7DerivedE
Type of *this: 4BaseI7DerivedE
Hello from Derived!

Типы this и *this имеют смысл, но я не понимаю, как успешен вызов hello. this не указывает на экземпляр Derived, так почему я могу привести тип this с Base<Derived> на Derived?

Обратите внимание, что я также заменил вызов на static_cast<T*>(this)->hello(); на вызов dynamic_cast<T*>(this)->hello();, и я все еще получаю те же результаты. Я ожидал, что dynamic_cast вернет nullptr, но это не так.

Я очень удивлен этими результатами. Спасибо за любую помощь, проясняя мои сомнения!

1 Ответ

0 голосов
/ 12 ноября 2018

Приведение, используемое для вызова hello(), имеет неопределенное поведение , когда T не соответствует истинному типу объекта, на который указывает this.Но hello() не получает доступа к чему-либо через this, поэтому не имеет значения, на что действительно указывает this.Вы можете так же легко сделать reinterpret_cast<T*>(12345)->hello(), и это все равно будет «работать».Однако вы решаете, что приведение this не будет иметь никакого значения, поскольку hello() просто игнорирует результат (в случае dynamic_cast, см. Не вызывает ли когда-либо вызов метода по нулевому указателю, который не обращается к каким-либо данным)? ).

Измените ваши классы, чтобы ввести элементы данных, к которым hello() пытается получить доступ через this, и вы увидите очень разные результаты (т. Е. Код, скорее всего, вылетит или сообщит об мусоре).и т. д.).

...