Это безумно общая версия решения @ Maxim.
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}
просто добавьте больше статических помощников типа «знает производный тип» в Exposer
для увеличения функциональности.
Живой пример .
Как использовать:
Создать тип Expose. Он должен иметь чистый виртуальный член и шаблонный класс реализации, который (учитывая класс, производный от типа Expose) реализует этот чистый виртуальный член.
Он должен наследовать от Expose<OwnType>
(CRTP), чтобы написать статический метод ::create
для вас.
Если вы хотите наследовать от дополнительных Expose
типов (т. Е. Создать два независимых интерфейса Expose, которые должны знать конкретный тип), вместо этого наследуйте от Expose< YourType, OtherExposeType, AnotherExposeType >
. Не наследуйте независимо от OtherExposeType
или AnotherExposeType
.
Если вы сделаете это, ваш шаблон Implement
не будет выбран.
Я мог бы улучшить это так, чтобы мы обнаружили Implement
шаблонов как у вас, так и у ваших баз, но это больше метапрограммирование, чем я сейчас планирую.