Сплит Paramter Pack - PullRequest
       15

Сплит Paramter Pack

4 голосов
/ 27 апреля 2019

Я хотел бы разделить пакет параметров шаблона. Что-то вроде этого. Как я мог сделать это?

template< typename... Pack >
struct TypeB : public TypeA< get<0, sizeof...(Pack)/2>(Pack...) >
             , public TypeA< get<sizeof...(Pack)/2, sizeof...(Pack)>(Pack...) > 
{
};

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

Ответы [ 2 ]

2 голосов
/ 28 апреля 2019

С std::tuple (C ++ 11) и std::index_sequence (C ++ 14, но есть обратные порты) , стандартная библиотека содержит все, чтобы эффективно и в некоторой степениудобно разделить пакет.

template <class, class, class>
struct TypeBImpl;
template <std::size_t... N, std::size_t... M, class T>
struct TypeBImpl<std::index_sequence<N...>, std::index_sequence<M...>, T>
: TypeA<typename std::tuple_element_t<T, N>::type...>
, TypeA<typename std::tuple_element_t<T, M + sizeof...(N)>::type...>
{};

template <class... Ts>
struct TypeB
: TypeBImpl<
    std::make_index_sequence<sizeof...(Ts) / 2>,
    std::make_index_sequence<(sizeof...(Ts) + 1) / 2>,
    std::tuple<std::enable_if<1, Ts>...>
>
{};

Для удобства я использовал промежуточную базу, которая имеет дополнительное преимущество, заключающееся в сохранении типов помощников для повторного использования в элементах.


Есливам это не нужно, или его существование в RTTI неудобно, есть другие решения:

template <class T, std::size_t N, std::size_t... M>
auto TypeA_part_impl(std::index_sequence<M...>)
-> TypeA<typename std::tuple_element_t<T, N + M>::type...>;

template <bool tail, class... Ts>
using TypeA_part = decltype(TypeA_part_impl<
    std::tuple<std::enable_if<1, Ts>...>,
    tail * sizeof...(Ts) / 2>(
    std::make_index_sequence<(sizeof...(Ts) + tail) / 2>()));

template <class... Ts>
struct TypeB : TypeA_part<0, Ts...>, TypeA_part<1, Ts...>
{
};

Я использую std::enable_if<1, T> в качестве удобного средства передачи произвольной информации о типе,даже если тип является настолько нерегулярным, он не может быть сохранен в std::tuple или является неполным.Не нужно определять свой собственный.

1 голос
/ 27 апреля 2019

Полагаю, есть много способов сделать это.

Если вы можете использовать хотя бы 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, "!");
 }
...