拆分参数包
Split paramter pack
我想拆分一个模板参数包。像这样的东西。
我该怎么做呢?
template< typename... Pack >
struct TypeB : public TypeA< get<0, sizeof...(Pack)/2>(Pack...) >
, public TypeA< get<sizeof...(Pack)/2, sizeof...(Pack)>(Pack...) >
{
};
以下是我对为什么这个问题不重复的看法:
我正在寻找一种通用的方法来执行此操作,该方法在将拆分包传递给其他模板 类 时会起作用 - 如上所示。以及将其传递给函数。
我想有很多方法可以做到这一点。
如果你至少可以使用 C++14,我建议使用 decltype()
和 std::tuple_cat()
的强大功能,如下所示:
(1) 声明(没有定义的原因,因为通过 decltype()
几个重载(和 SFINAE enabled/disabled)使用如下
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 ();
想法是 return 当索引在正确范围内时 std::tuple<T>
,否则 std::tuple<>
。
(2) 定义一个助手 class 将 std::tuple<Ts...>
转换为 TypeA<Ts...>
template <typename>
struct pta_helper2;
template <typename ... Ts>
struct pta_helper2<std::tuple<Ts...>>
{ using type = TypeA<Ts...>; };
(3) 定义一个帮助程序 class,它仅在元组中连接正确范围内的类型
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
类型以更简单的方式使用前面的助手 class
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
(C++11) and std::index_sequence
(C++14, but there are backports),标准库包含了所有东西,可以高效且方便地拆分包。
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
中或不完整。无需自己定义。
我想拆分一个模板参数包。像这样的东西。 我该怎么做呢?
template< typename... Pack >
struct TypeB : public TypeA< get<0, sizeof...(Pack)/2>(Pack...) >
, public TypeA< get<sizeof...(Pack)/2, sizeof...(Pack)>(Pack...) >
{
};
以下是我对为什么这个问题不重复的看法: 我正在寻找一种通用的方法来执行此操作,该方法在将拆分包传递给其他模板 类 时会起作用 - 如上所示。以及将其传递给函数。
我想有很多方法可以做到这一点。
如果你至少可以使用 C++14,我建议使用 decltype()
和 std::tuple_cat()
的强大功能,如下所示:
(1) 声明(没有定义的原因,因为通过 decltype()
几个重载(和 SFINAE enabled/disabled)使用如下
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 ();
想法是 return 当索引在正确范围内时 std::tuple<T>
,否则 std::tuple<>
。
(2) 定义一个助手 class 将 std::tuple<Ts...>
转换为 TypeA<Ts...>
template <typename>
struct pta_helper2;
template <typename ... Ts>
struct pta_helper2<std::tuple<Ts...>>
{ using type = TypeA<Ts...>; };
(3) 定义一个帮助程序 class,它仅在元组中连接正确范围内的类型
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
类型以更简单的方式使用前面的助手 class
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
(C++11) and std::index_sequence
(C++14, but there are backports),标准库包含了所有东西,可以高效且方便地拆分包。
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
中或不完整。无需自己定义。