Поврежденная переменная-член в производном классе с Curious Recurring Templating Pattern - PullRequest
0 голосов
/ 06 ноября 2018

В настоящее время я играю с CRTP и сталкиваюсь с проблемой испорченной переменной-члена в производном классе, имеющей значение мусора (в настоящее время существует 4 уровня полиморфизма, причем самый верхний базовый класс называется «A»). "и самый нижний наиболее производный класс называется" D ").

Вот некоторый код, который отображает пример этой проблемы:

//A.hpp
template <class TB>
class A {
public:
    A();
    void CRTP_func();
};

template <class TB>
A<TB>::A() {
    std::cout << "A constructor called!" << std::endl;
}

template<class TB>
void A<TB>::CRTP_func() {
    std::cout << "CRTP_index called in A" << std::endl;
    static_cast<TB*>(this)->CRTP_func2();
}

//B.hpp
#include "A.hpp"
#include <vector>

template<class TC>
class B : public A<B<TC>>
{
public:
    B();
    void CRTP_func2();
};

template<class TC>
B<TC>::B() {
    std::cout << "B constructor called!" << std::endl;
}

template<class TC>
void B<TC>::CRTP_func2() {
    std::cout << "CRTP_func called in B" << std::endl;
    static_cast<TC*>(this)->CRTP_func3();
}

//C.hpp
#include "B.hpp"

template<class TD>
class C : B<C<TD>> {
public:
    C();
    void CRTP_func3();
    int x;
};

template<class TD>
C<TD>::C() {
    std::cout << "C constructor called" << std::endl;
}

template<class TD>
void C<TD>::CRTP_func3() {
    std::cout << "CRTP_index3 called in C" << std::endl;
    static_cast<TD*>(this)->CRTP_func4();
}


//D.hpp
#include "C.hpp"

    class D : C<D> {
    public:
        D();
        bool onInit();
        void CRTP_func4();
        C<D> top;
        int y = 0;

    };

D::D() {
    std::cout << "D constructor called!" << std::endl;
}

bool D::onInit() {
    std::cout << "D onInit called!" << std::endl;
    y = 5;
    return true;
}

void D::CRTP_func4() {
    std::cout << y << std::endl;
    std::cout << "CRTP_index4 called in D! " << std::endl;
}

//main.hpp
int main {
D * D_ptr = new D();
    D_ptr->onInit();
    D_ptr->top.CRTP_func3();
    return 0;
}

Как видите, A - это базовый класс, а D - это производный класс как таковой:

A<B<C<D>>>

Вывод этой программы следующий:

A constructor called!
B constructor called!
C constructor called
A constructor called!
B constructor called!
C constructor called
D constructor called!
D onInit called!
CRTP_index3 called in C
-33686019
CRTP_index4 called in D!

Значение -33686019 распечатывается в D.hpp, где значение y печатается и устанавливается на 5 при инициализации. После небольшого копания я проверил значение в main.cpp, и оно установлено на 5 даже после выполнения этих вызовов CRTP, но выводится значение мусора.

После еще одной отладки я понял, что удаление строки

int x;

в B.hpp решает эту проблему, поэтому я думаю, что проблема связана с некоторым смещением, но я не уверен, почему это произойдет. Кто-нибудь знает, почему это произошло или как это исправить?

Извините за длинный пост и неоднозначный код, я попытался убрать большую часть сложности и максимально упростить код ради поста.

UPDATE:

Благодаря комментариям ниже я понял, как исправить мою проблему. Вместо использования D::top лучшим подходом является создание указателя в главном файле следующим образом:

C<D> * C_ptr = static_cast<C<D>*>(D_ptr);

, а затем позвоните CRTP_func3() оттуда так:

C_ptr->CRTP_func3();

Работает как задумано.

Ответы [ 2 ]

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

Самая фундаментальная проблема, с точки зрения логики, заключается в том, что вы ожидаете, что D_Ptr и D_Ptr->top будут иметь одинаковое значение для y (вы сказали, что ожидали 5). D_Ptr->top - это совершенно другой экземпляр, и даже если он в конечном итоге получен из D, он будет иметь свою собственную копию y.

Тогда D происходит от C, поэтому для C принципиально невозможно получить D, независимо от безумия шаблона. Это предположение вы делаете, вызывая CRTP_func4 на this указателе C.

Кроме того, тот же самый вызов функции предполагает, что тип шаблона TD является экземпляром D. Этот вызов функции существует в C, и это безумное предположение для C, хотя я полагаю, что в данном случае это правильно. ( это тот, который компилятор поймал бы, если бы его не было)

И, наконец, относительно crtp: подумайте об отказе от сатаны и всех его путей.

А если серьезно, то, очевидно, нет полной замены, но я думаю, вы найдете, если вы полностью учитываете мощь интерфейсов (или pure abstract classes в C ++), вы , возможно, сможете найти способ обойти это. И имеют (почти) одинаковую производительность ... Конечно, я не знаю вашей конкретной проблемы, но я настоятельно рекомендую внимательно и внимательно изучить эту статью https://en.wikipedia.org/wiki/Composition_over_inheritance

В частности, посмотрите на 2-й пример блока кода, который написан на C # (где interface будет pure abstract class на C ++). Подумайте, может ли этот шаблон помочь вам.

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

Вы вызываете функцию CRTP_func3() для объекта со статическим типом C<D> (D::top). Функция C<D>::CRTP_func3() делает static_cast<D*>(this), но объект не имеет ожидаемого типа. Таким образом, поведение не определено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...