Как пересылать конструкторы с CRTP и множественным наследованием C ++ - PullRequest
0 голосов
/ 21 апреля 2019

Недавно я копался в использовании CRTP для клонирования объектов с использованием ковариации в c ++ 11 и обнаружил следующее: https://www.fluentcpp.com/2017/09/12/how-to-return-a-smart-pointer-and-use-covariance/ Пост понятен, и он работает, в некотором роде.В посте не говорится о том, как использовать параметры в конструкторах.Я получил виртуальный трюк с использованием Base :: Base, хотя он заставляет пользователя явно вызывать конструкторы базы при создании экземпляра класса листа.Это требуется, только если вы ищете что-то вроде алмазного наследования.

Хотя в более обычных случаях, когда у вас есть несколько уровней наследования, у вас есть что-то вроде этого: abstract A -> abstract B ->абстрактный C -> конкретный D. Я понимаю, почему только листовой класс должен быть инстанцируемым.Но на каждом уровне конструкторы становятся все более и более конкретными, и с приведенным мною примером разработки CRTP я не понимаю, как это сделать.

В дальнейшем то, что я придумал, это упрощение:один базовый класс:

// Virtual and abstract tricks
template <typename Base>
class Virtual : virtual public Base
{
public:
    using Base::Base;
};
template <typename Object>
class Abstract
{
};

// General CRTP : Inheritance + implementation of clone function
template <typename Object, typename Base=void>
class Clone : public Base
{
public:
    virtual ~Clone() =default;
    std::unique_ptr<Object> clone() const {
        return std::unique_ptr<Object>(static_cast<Object *>(this->copy()));
    }
private:
    virtual Clone * copy() const override {
        return new Object(static_cast<const Object &>(*this));
    }
};

// Specialization : Inheritance + an pure cloning function
template <typename Object>
class Clone<Abstract<Object>>
{
public:
    virtual ~Clone() =default;
    std::unique_ptr<Object> clone() const {
        return std::unique_ptr<Object>(static_cast<Object *>(this->copy()));
    }
private:
    virtual Clone *copy() const =0;
};

И в отношении моих тестовых классов:

class TestB0 : public Clone<Abstract<TestB0>>
{
public:
    TestB0() : B0(new int(0)) {cout<<"crea B0 vide"<<endl;}
    TestB0(int xB0) : B0(new int(xB0)) {cout<<"crea B0 "<<xB0<<" : "<<*B0<<endl;}
    TestB0(const TestB0 &t) : B0(new int(*t.B0)) {cout<<"copy B0 "<<*B0<<endl;}
    virtual ~TestB0() {delete B0;}
    void setB0(int xB0) {*B0=xB0;}
    int getB0() {return *B0;}
private:
    int *B0;
};

class TestB1 : public Clone<TestB1, TestB0>
{
public:
    TestB1() : B1(new int(0)) {cout<<"crea B1 vide"<<endl;}
    TestB1(int xB0=11, int xB1=20) : B1(new int(xB1)) {cout<<"crea B1 "<<xB0<<" "<<xB1<<" : "<<getB0()<<" "<<*B1<<endl;}
    TestB1(const TestB1 &t) : B1(new int(*t.B1)) {cout<<"copy B1 "<<getB0()<<" "<<*B1<<endl;}
    virtual ~TestB1() {delete B1;}
    void setB1(int xB1) {*B1=xB1;}
    int getB1() {return *B1;}
private:
    int *B1;
};

Когда я создаю экземпляр TestB1, у меня нет доступа к конструктору TestB0.Для этого я могу использовать виртуальный трюк:

class TestB1 : public Clone<TestB1, Virtual<TestB0>>

Но когда я это сделаю, я должен явно вызвать конструктор в листовом классе.Это не проблема, когда у меня только один уровень наследования.Я думаю, что когда у меня есть больше.

Я не могу найти способ заставить все это работать элегантно.Есть ли способ инициализировать базовый класс без использования виртуальной базы?Как обычно, когда CRTP не используется.

Спасибо за ваши ответы.

В качестве комментария: я создаю полиморфные классы для библиотеки, из которой я хочу оптимизироваться.точка зрения времени исполнения (без виртуальных таблиц).Я могу работать только с C ++ 11, поэтому использование объявления с расширением пакета не будет работать, как в C ++ 17.

...