Сплит Paramter Pack

72
7

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

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

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

спросил(а) 2019-04-27T22:35:00+03:00 11 месяцев, 2 недели назад
1
Решение
52

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

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 или он неполный. Не нужно определять свое.

ответил(а) 2019-04-28T00:24:00+03:00 11 месяцев, 2 недели назад
36

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

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

Ниже приведен пример полной компиляции С++ 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, "!");
}

ответил(а) 2019-04-27T23:37:00+03:00 11 месяцев, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема