Удовлетворение чистого контракта виртуальной функции с наследованием класса шаблона

Извиняюсь, если на этот вопрос уже был дан ответ; Я искал Stack Overflow, но не мог найти ничего подобного. У меня есть ощущение, что то, что я спрашиваю, невозможно, но я уверен, что должен быть способ достичь этого.

Я работаю с базовым классом, который содержит ряд чисто виртуальных функций, сгруппированных в логически связанные группы. Вместо того, чтобы просто реализовывать их в моем производном классе, я в идеале хотел бы сгруппировать функции в классы, имеющие дело со связанной функциональностью, а затем перенести их в мой производный класс.

Я пробовал это использовать (упрощенный пример ниже), но получаю ошибку ниже:

// The base class - I can't change this
class Base {
    public:
    virtual void toImplement(double a) = 0;
};

// Implements logically grouped functionality required by the base class
class Implementor {
    public:
    virtual void toImplement(double a) {}
};

// Concrete derived class, satisfying Base functional requirements by
// (somehow) drawing on the Implementor class functionality.
template <typename Type>
class Derived : public Base, Type {
};

int main() {
    Derived<Implementor> a; // Fails
}

Это терпит неудачу с ошибкой:

error: variable type 'Derived<Implementor>' is an abstract class
    Derived<Implementor> a;
                         ^
note: unimplemented pure virtual method 'toImplement' in 'Derived'
    virtual void toImplement(double a) = 0;

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

2 ответа

Решение

Если я правильно понимаю вопрос, вы хотите использовать другой класс для внедрения реализованных методов.

Вам просто нужно делегировать вызов функции разработчикам. Следующий код является более общим в том смысле, что он может объединять множество разработчиков.

Примечание: это в C++17 из-за выражения сгиба. Но вы можете легко выполнить эту функциональность до C++17.

#include <tuple>
#include <iostream>
#include <memory>

struct Base {
    virtual void toImplement(double a) = 0;
};

template <class... Impls>
struct Derived : public Base {
    virtual void toImplement(double a) override {
        do_all(a, std::index_sequence_for<Impls...>{});
    }
    std::tuple<Impls...> impls;
private:
    template<std::size_t... Is>
    void do_all(double a, std::index_sequence<Is...>) {
        (std::get<Is>(impls).do_(a), ...);
    }
};

// test
struct Implementor1 {
    void do_(double a) { std::cout << "first impl do: " << a << "\n"; }
};

struct Implementor2 {
    void do_(double a) { std::cout << "second impl do: " << a << "\n"; }
};

int main() {
    std::unique_ptr<Base> ptr = std::make_unique<Derived<Implementor1, Implementor2>>();

    ptr->toImplement(2.3);
}

Если вам нужно иметь дело с наследством страшных алмазов, вот как вы это делаете:

class Base {
    public:
    virtual void toImplement(double a) = 0;
};

class Implementor : public virtual Base {
    public:
    virtual void toImplement(double a) {}
};

template <typename Type>
class Derived : public virtual Base, virtual Type {
};

int main() {
    Derived<Implementor> a; // Fails
}

То, как у вас это сейчас, toImplement в Implementor не имеет ничего общего со случайно названной функцией в Base,

Другие вопросы по тегам