Полагаю, есть много способов сделать это.
Если вы можете использовать хотя бы C ++ 14, я предлагаю использовать мощности decltype()
и std::tuple_cat()
следующим образом:
(1) объявляют (нет причин для определения, потому что используются через decltype()
пару перегруженных (и SFINAE включен / отключен) следующим образом
template <std::size_t Imin, std::size_t Imax, std::size_t I, typename T>
std::enable_if_t<(Imin <= I) && (I < Imax), std::tuple<T>> getTpl ();
template <std::size_t Imin, std::size_t Imax, std::size_t I, typename>
std::enable_if_t<(I < Imin) || (Imax <= I), std::tuple<>> getTpl ();
Идея состоит в том, чтобы возвращать std::tuple<T>
, когда индекс находится в нужном диапазоне, в противном случае std::tuple<>
.
(2) определяет вспомогательный класс для преобразования std::tuple<Ts...>
в TypeA<Ts...>
template <typename>
struct pta_helper2;
template <typename ... Ts>
struct pta_helper2<std::tuple<Ts...>>
{ using type = TypeA<Ts...>; };
(3) определяет вспомогательный класс, который объединяет в кортеже только типы в правильном диапазоне
template <std::size_t, std::size_t, typename ... Ts>
struct pta_helper1;
template <std::size_t I0, std::size_t I1, std::size_t ... Is, typename ... Ts>
struct pta_helper1<I0, I1, std::index_sequence<Is...>, Ts...>
: public pta_helper2<decltype(std::tuple_cat(getTpl<I0, I1, Is, Ts>()...))>
{ };
Идея заключается в объединении последовательности std::tuple<>
и std::tuple<T>
, где типы T
являются типом внутри запрошенного диапазона; результирующий тип (аргумент шаблона pta_helper2
) - это std::tuple<Us...>
, где Us...
- это в точности типы в запрошенном диапазоне.
(4) определяет тип using
для более простого использования предыдущего вспомогательного класса
template <std::size_t I0, std::size_t I1, typename ... Ts>
using proTypeA = typename pta_helper1<
I0, I1, std::make_index_sequence<sizeof...(Ts)>, Ts...>::type;
(5) теперь ваши TypeB
просто становятся
template <typename ... Ts>
struct TypeB : public proTypeA<0u, sizeof...(Ts)/2u, Ts...>,
public proTypeA<sizeof...(Ts)/2u, sizeof...(Ts), Ts...>
{ };
Ниже приведен пример полной компиляции C ++ 14
#include <tuple>
#include <type_traits>
template <typename ...>
struct TypeA
{ };
template <std::size_t Imin, std::size_t Imax, std::size_t I, typename T>
std::enable_if_t<(Imin <= I) && (I < Imax), std::tuple<T>> getTpl ();
template <std::size_t Imin, std::size_t Imax, std::size_t I, typename>
std::enable_if_t<(I < Imin) || (Imax <= I), std::tuple<>> getTpl ();
template <typename>
struct pta_helper2;
template <typename ... Ts>
struct pta_helper2<std::tuple<Ts...>>
{ using type = TypeA<Ts...>; };
template <std::size_t, std::size_t, typename ... Ts>
struct pta_helper1;
template <std::size_t I0, std::size_t I1, std::size_t ... Is, typename ... Ts>
struct pta_helper1<I0, I1, std::index_sequence<Is...>, Ts...>
: public pta_helper2<decltype(std::tuple_cat(getTpl<I0, I1, Is, Ts>()...))>
{ };
template <std::size_t I0, std::size_t I1, typename ... Ts>
using proTypeA = typename pta_helper1<
I0, I1, std::make_index_sequence<sizeof...(Ts)>, Ts...>::type;
template <typename ... Ts>
struct TypeB : public proTypeA<0u, sizeof...(Ts)/2u, Ts...>,
public proTypeA<sizeof...(Ts)/2u, sizeof...(Ts), Ts...>
{ };
int main()
{
using tb = TypeB<char, short, int, long, long long>;
using ta1 = TypeA<char, short>;
using ta2 = TypeA<int, long, long long>;
static_assert(std::is_base_of<ta1, tb>::value, "!");
static_assert(std::is_base_of<ta2, tb>::value, "!");
}