【问题标题】:Split paramter pack拆分参数包
【发布时间】:2019-04-27 19:35:38
【问题描述】:

我想拆分一个模板参数包。像这样的东西。 我该怎么做呢?

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++ templates variadic-templates template-meta-programming template-inheritance


【解决方案1】:

使用std::tuple (C++11)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&lt;1, T&gt; 作为传输任意类型信息的便捷工具,即使类型非常不规则,它也无法存储在std::tuple 中,或者不完整。无需自己定义。

【讨论】:

    【解决方案2】:

    我想有很多方法可以做到这一点。

    如果你至少可以使用 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&lt;T&gt;,否则返回std::tuple&lt;&gt;

    (2) 定义一个帮助类将std::tuple&lt;Ts...&gt; 转换为TypeA&lt;Ts...&gt;

    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&lt;&gt;std::tuple&lt;T&gt;的序列,其中T类型是请求范围内的类型;结果类型(pta_helper2 的模板参数)是 std::tuple&lt;Us...&gt;,其中 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, "!");
     }
    

    【讨论】:

      猜你喜欢
      • 2017-03-28
      • 2014-07-01
      • 2021-07-14
      • 2014-02-05
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多