CRTP на помощь!
template<class T, class D, class Base, class=void>
struct Container_getA:Base {};
template<class T, class D, class Base, class=void>
struct Container_getB:Base {};
template<class T, class D, class Base>
struct Container_getA<T, D, Base, std::enable_if_t<std::is_base_of<TypeA,T>{}>>:
Base
{
TypeA* get_TypeA() final { return self()->ptr; }
D* self() { return static_cast<D*>(this); }
};
template<class T, class D, class Base>
struct Container_getB<T, D, Base, std::enable_if_t<std::is_base_of<TypeB,T>{}>>:
Base
{
TypeB* get_TypeB() final { return self()->ptr; }
D* self() { return static_cast<D*>(this); }
};
template <class T>
struct Container:
Container_getA< T, Container<T>,
Container_getB< T, Container<T>,
Container_base
>
>
{
Container(): ptr(new T()) {}
public: // either public, or complex friend declarations; just make it public
T* ptr;
};
и готово.
Вы можете проделать небольшую работу, чтобы разрешить:
struct Container: Bases< T, Container<T>, Container_getA, Container_getB, Container_getC >
или тому подобное, где мысложите базы CRTP.
Вы также можете очистить свой синтаксис:
template<class...Ts>
struct types {};
template<class T>
struct tag_t {using type=T;};
template<class T>
constexpr tag_t<T> tag{};
Тогда вместо набора именованных получателей получите:
template<class List>
struct Container_getters;
template<class T>
struct Container_get {
virtual T* get( tag_t<T> ) { return nullptr; }
};
template<class...Ts>
struct Container_getters<types<Ts...>>:
Container_get<Ts>...
{
using Container_get<Ts>::get...; // C++17
template<class T>
T* get() { return get(tag<T>); }
};
и теперь центральный список типов может использоваться для поддержки набора типов, которые вы можете получить из контейнера.
Затем мы можем использовать этот центральный список типов для написания промежуточных помощников CRTP.
template<class Actual, class Derived, class Target, class Base, class=void>
struct Container_impl_get:Base {};
template<class Actual, class Derived, class Target, class Base>
struct Container_impl_get<Actual, Derived, Target, Base,
std::enable_if_t<std::is_base_of<Target, Actual>{}>
>:Base {
using Base::get;
virtual Target* get( tag_t<Target> ) final { return self()->ptr; }
Derived* self() { return static_cast<Derived*>(this); }
};
и теперь нам просто нужно написать механизм сгиба.
template<class Actual, class Derived, class List>
struct Container_get_folder;
template<class Actual, class Derived, class List>
using Container_get_folder_t=typename Container_get_folder<Actual, Derived, List>::type;
template<class Actual, class Derived>
struct Container_get_folder<Actual, Derived, types<>> {
using type=Container_base;
};
template<class Actual, class Derived, class T0, class...Ts>
struct Container_get_folder<Actual, Derived, types<T0, Ts...>> {
using type=Container_impl_get<Actual, Derived, T0,
Container_get_folder_t<Actual, Derived, types<Ts...>>
>;
};
, чтобы мы получили
using Container_types = types<TypeA, TypeB, TypeC>;
struct Container_base:Container_getters<Container_types> {
};
template <typename T>
struct Container: Container_get_folder_t<T, Container<T>, Container_types>
{
Container(): ptr(new T()) {}
T* ptr;
};
, и теперь мы можем расширить это, просто добавив тип к Container_types
.
Вызывающие абоненты, которым нужен определенный тип, могут сделать:
Container_base* ptr = /* whatever */;
ptr->get<TypeA>()
или
ptr->get(tag<TypeA>);
оба работают одинаково хорошо.
Живой пример - он использует одну или две функции C ++ 14 (а именно шаблоны переменных в tag
), но вы можете заменить tag<X>
на tag_t<X>{}
.