Я хотел бы разделить пакет параметров шаблона. Что-то вроде этого. Как я мог это сделать?
template< typename... Pack >
struct TypeB : public TypeA< get<0, sizeof...(Pack)/2>(Pack...) >
, public TypeA< get<sizeof...(Pack)/2, sizeof...(Pack)>(Pack...) >
{
};
Вот мой взгляд на то, почему этот вопрос не является дубликатом: Я ищу общий способ сделать это, который будет работать при передаче разделенного пакета в другие классы шаблонов, как показано выше. А также передать его функциям.
Мне очень нравится ответ max66, потому что его использование очень чистое и легко читаемое. Я ожидаю, что смогу передать TypeA в качестве другого параметра шаблона.





Я полагаю, что есть много способов сделать это.
Если вы можете использовать хотя бы 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, "!");
}
С std::tuple (С++ 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 или он неполный. Не нужно определять мой собственный.
Возможный дубликат Разделить argpack пополам?