Удовлетворение чистого контракта виртуальной функции с наследованием класса шаблона
Извиняюсь, если на этот вопрос уже был дан ответ; Я искал 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
,