【问题标题】:Generating a template pack生成模板包
【发布时间】:2015-09-05 05:01:25
【问题描述】:

Generate<P<3>, P<5,0>, P<4,0,0>, P<3,0,1>>::type

Pack< A<0>, A<0,0>, A<0,0,0>, A<0,0,1>, A<0,0,2>, A<0,0,3>, A<0,1>, A<0,1,0>, A<0,1,1>, A<0,1,2>, A<0,2>, A<0,3>, A<0,4>, A<1>, A<2> >

因为P&lt;3&gt; 表示P&lt;n&gt; 对于n = 0,1,2 存在; P&lt;5,0&gt; 表示在 n = 0,1,2,3,4 时存在 A&lt;0,n&gt;P&lt;4,0,0&gt; 表示在 n = 0,1,2,3 时存在 A&lt;0,0,n&gt;P&lt;3,0,1&gt; 表示在 n = 0,1,2 时存在 A&lt;0,1,n&gt;。 在排序方面,如果 x A<n1,n2,n3,...,nk,x,...> 将始终在 A&lt;n1,n2,n3,...,nk,y,...&gt; 之前,A&lt;n1,n2,n3,...,nk&gt; 将始终在 A&lt;n1,n2,n3,...,nk, ...&gt; 之前,其中第二个省略号 ... 非空。 所以需要写出Generate&lt;Packs...&gt;的实现。

这样做的动机:如果template &lt;int... Is&gt; class Object 对其Is... 包具有某些可能性,由上面的 3、5、4 和 3 等常量定义,那么包含所有可能类型的包将允许 通过迭代包生成特定的Object&lt;Is...&gt; 实例。

这是我目前所拥有的:

#include <iostream>
#include <type_traits>

template <int...> class A;
template <typename...> struct Pack;
template <int...> struct P;

template <int N, int Count, typename Front, typename Output> struct GenerateHelper;

template <int N, int Count, int... Is, typename... As>
struct GenerateHelper<N, Count, P<Is...>, Pack<As...>> :
    GenerateHelper<N, Count + 1, P<Is...>, Pack<As..., A<Is..., Count>>> {};

template <int N, int... Is, typename... As>
struct GenerateHelper<N, N, P<Is...>, Pack<As...>> {
    using type = Pack<As...>;
};

template <typename...> struct Generate;

// Simple special case just to start off.  Generate has only one pack to deal with.
template <int N, int... Is>
struct Generate<P<N, Is...>> : GenerateHelper<N, 0, P<Is...>, Pack<>> {};

int main() {
    using T = Generate<P<3,0,0>>::type;
    std::cout << std::is_same<T, Pack<A<0,0,0>, A<0,0,1>, A<0,0,2>>>::value << '\n'; // true
}

但现在我被 Generate 中只有 2 包的情况所困扰。谁能帮我继续?

想法:2个包,单独生成,合并两个Packs,然后排序后?但我认为排序将是最难的部分。

【问题讨论】:

    标签: c++ templates c++11 variadic


    【解决方案1】:

    诀窍是意识到您从每个“生成”过程中获得的序列已经排序,问题简化为合并几个排序列表。

    为简单起见,我制作了 APackP 空结构。

    template <int...> class A {};
    template <typename...> struct Pack {};
    template <int...> struct P {};
    

    从一个P生成一组As:

    template<int I, int... Tail>
    auto do_sequence_for(P<I, Tail...>) -> std::make_integer_sequence<int, I>;
    
    template<class PP>
    using sequence_for = decltype(do_sequence_for(PP()));
    
    template<int I, int... Front, int... Tail>
    auto do_generate_single(P<I, Front...>, std::integer_sequence<int, Tail...>)
         -> Pack<A<Front..., Tail>...>;
    
    template<class PP>
    using generate_single = decltype(do_generate_single(PP(), sequence_for<PP>()));
    

    两个As 的字典比较:

    template<class A1, class A2>
    struct compare; // returns A1 < A2
    
    template<int I, int J, int...Is, int...Js>
    struct compare<A<I, Is...>, A<J, Js...>> : std::integral_constant<bool, I < J> {};
    
    template<int I, int...Is, int...Js>
    struct compare<A<I, Is...>, A<I, Js...>> : compare<A<Is...>, A<Js...>> {};
    
    template<int...Is>
    struct compare<A<Is...>, A<>> : std::false_type {};
    
    template<int J, int...Js>
    struct compare<A<>, A<J, Js...>> : std::true_type {};
    

    合并两个已排序的 As 包:

    template<class Pack1, class Pack2, class Result=Pack<>>
    struct merge2;
    
    template<class A1, class...A1s, class A2, class...A2s, class...R>
    struct merge2<Pack<A1, A1s...>, Pack<A2, A2s...>, Pack<R...>>
        : std::conditional_t<compare<A1, A2>::value,
                             merge2<Pack<A1s...>, Pack<A2, A2s...>, Pack<R..., A1>>,
                             merge2<Pack<A1, A1s...>, Pack<A2s...>, Pack<R..., A2>>>
    {};
    
    template<class...A1s, class...R>
    struct merge2<Pack<A1s...>, Pack<>, Pack<R...>>
    {
        using type = Pack<R..., A1s...>;
    };
    
    template<class A2, class...A2s, class...R>
    struct merge2<Pack<>, Pack<A2, A2s...>, Pack<R...>>
    {
        using type = Pack<R..., A2, A2s...>;
    };
    

    合并多个已排序的As 包:

    template<class... Packs>
    struct merge;
    
    template<class P1>
    struct merge<P1> {
        using type = P1;
    };
    
    template<class P1, class P2, class... Ps>
    struct merge<P1, P2, Ps...> : merge<typename merge2<P1, P2>::type, Ps...> {};
    

    把它们放在一起:

    template<class...Ps>
    struct Generate{
        using type = typename merge<generate_single<Ps>...>::type;
    };
    

    Demo.

    【讨论】:

      【解决方案2】:

      不幸的是,Visual Studio 2015 不会编译 T.C. 的优秀解决方案(由于它有一些错误),因此我改编了以下内容,在 Visual Studio 2015 和 GCC 5.1.0 上都可以编译(并且也可以推广到任何整数类型,任何类等...)。只需要替换他的generate_single 函数。

      #include <iostream>
      #include <utility>
      #include <type_traits>
      
      // Another way to generate a pack of A's from one P than the above.
      template <typename T, template<T...> class Class, T N, T Count, typename Front, typename Output> struct GenerateSingleHelper;
      
      template <typename T, template<T...> class Class, T N, T Count, template <T...> class Z, T... Is, template <typename...> class PackOfPacks, typename... As>
      struct GenerateSingleHelper<T, Class, N, Count, Z<Is...>, PackOfPacks<As...>> : GenerateSingleHelper<T, Class, N, Count + 1, Z<Is...>, PackOfPacks<As..., Class<Is..., Count>>> {};
      
      template <typename T, template<T...> class Class, T N, template <T...> class Z, T... Is, template <typename...> class PackOfPacks, typename... As>
      struct GenerateSingleHelper<T, Class, N, N, Z<Is...>, PackOfPacks<As...>> {
          using type = PackOfPacks<As...>;
      };
      
      template <typename T, template<T...> class Class, template <typename...> class PackOfPacks, typename> struct GenerateSingle;
      
      template <typename T, template<T...> class Class, template <typename...> class PackOfPacks, template <T...> class Z, T N, T... Is>
      struct GenerateSingle<T, Class, PackOfPacks, Z<N, Is...>> : GenerateSingleHelper<T, Class, N, 0, Z<Is...>, PackOfPacks<>> {};
      
      // Lexicographical comparison of two A's.
      template <typename T, typename A1, typename A2> struct Compare;  // Determines if A1 < A2.
      
      template <typename T, template <T...> class Pack, T I, T J, T... Is, T... Js>
      struct Compare<T, Pack<I, Is...>, Pack<J, Js...>> : std::integral_constant<bool, I < J> {};
      
      template <typename T, template <T...> class Pack, T I, T... Is, T... Js>
      struct Compare<T, Pack<I, Is...>, Pack<I, Js...>> : Compare<T, Pack<Is...>, Pack<Js...>> {};
      
      template <typename T, template <T...> class Pack, T... Is>
      struct Compare<T, Pack<Is...>, Pack<>> : std::false_type {};
      
      template <typename T, template <T...> class Pack, T J, T... Js>
      struct Compare<T, Pack<>, Pack<J, Js...>> : std::true_type {};  // J is needed to indicate that Pack<J, Js...> is not empty.
      
      // Merging two sorted packs of A's.
      template <typename T, template <typename...> class PackOfPacks, typename PackOfPacks1, typename PackOfPacks2, typename Result = PackOfPacks<>> struct MergeTwoPacks;
      
      template <typename T, template <typename...> class PackOfPacks, typename A1, typename... A1s, typename A2, typename... A2s, typename... Accumulated>
      struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1, A1s...>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated...>> : std::conditional_t<Compare<T, A1, A2>::value,
          MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1s...>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated..., A1>>,
          MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1, A1s...>, PackOfPacks<A2s...>, PackOfPacks<Accumulated..., A2>>> {};
      
      template <typename T, template <typename...> class PackOfPacks, typename... A1s, typename... Accumulated>
      struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1s...>, PackOfPacks<>, PackOfPacks<Accumulated...>> {
          using type = PackOfPacks<Accumulated..., A1s...>;  // Since PackOfPacks<A1s...> is already sorted.
      };
      
      template <typename T, template <typename...> class PackOfPacks, typename A2, typename... A2s, typename... Accumulated>
      struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated...>> {  // A2 is needed to indicate that PackOfPacks<A2, A2s...> is not empty.
          using type = PackOfPacks<Accumulated..., A2, A2s...>;  // Since PackOfPacks<A2s...> is already sorted.
      };
      
      // Merging any number of sorted packs of A's.
      template <typename T, template <typename...> class PackOfPacks, typename... Packs> struct Merge;
      
      template<typename T, template <typename...> class PackOfPacks, typename First, typename Second, typename... Rest>
      struct Merge<T, PackOfPacks, First, Second, Rest...> : Merge<T, PackOfPacks, typename MergeTwoPacks<T, PackOfPacks, First, Second>::type, Rest...> {};
      
      template <typename T, template <typename...> class PackOfPacks, typename Last>
      struct Merge<T, PackOfPacks, Last> {
          using type = Last;
      };
      
      // Putting it all together.
      template <typename T, template <T...> class Class, template <typename...> class PackOfPacks, typename... Packs>
      struct Generate : Merge<T, PackOfPacks, typename GenerateSingle<T, Class, PackOfPacks, Packs>::type...> {};
      
      // Testing.
      template <int...> class A {};
      template <typename...> struct PackOfPacks;
      template <int...> struct P;
      
      int main() {
          std::cout << std::boolalpha << std::is_same<
              Generate<int, A, PackOfPacks, P<3>, P<5,0>, P<4,0,0>, P<3,0,1>>::type,
              PackOfPacks< A<0>, A<0,0>, A<0,0,0>, A<0,0,1>, A<0,0,2>, A<0,0,3>, A<0,1>, A<0,1,0>, A<0,1,1>, A<0,1,2>, A<0,2>, A<0,3>, A<0,4>, A<1>, A<2> >
          >::value << '\n';  // true
      }
      

      【讨论】:

        猜你喜欢
        • 2014-07-14
        • 2018-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-07
        • 2018-07-14
        相关资源
        最近更新 更多