簡體   English   中英

創建兩個可變參數的非類型模板參數包的笛卡爾乘積展開

[英]Create cartesian product expansion of two variadic, non-type template parameter packs

可以說,我有

  • 兩類非類型模板參數(可能具有不同的類型)
  • 模板foo ,該模板foo將每個列表中的一個值作為參數

如何創建變量foo的可變參數包,並使用兩個列表元素的笛卡爾乘積進行參數化?

這是我的意思:

template<int ...>
struct u_list {};

template<char ...>
struct c_list {};

template<int, char >
struct foo {};

template<class ...>
struct bar {};

using int_vals = u_list<1, 5, 7>;
using char_vals = c_list<-3, 3>;


using result_t = /* magic happens*/
using ref_t = bar<
    foo<1, -3>, foo<1, 3>,
    foo<5, -3>, foo<5, 3>,
    foo<7, -3>, foo<7, 3>
>;

static_assert(std::is_same<result_t, ref_t >::value, "");

我正在尋找一種可以在c ++ 11中工作並且不使用除c ++ 11標准庫之外的任何庫的解決方案。 我還擁有c ++ 14的人工版本的index_sequence / make_index_sequence並且可以簡化數組,並可以將非類型參數列表作為數組提供。

到目前為止,我發現的最接近的是: 如何創建類型列表的笛卡爾積? 因此,原則上(我尚未測試過),應該可以將非類型參數包轉換為類型參數包,然后在鏈接的帖子中應用解決方案,但是我希望能夠有一個更簡單/更短的解決方案這行:

template<int... Ints, char ... Chars>
auto magic(u_list<Ints...>, c_list<Chars...>) 
{
    //Doesn't work, as it tries to expand the parameter packs in lock step
    return bar<foo<Ints,Chars>...>{};  
}

using result_t = decltype(magic(int_vals{}, char_vals{}));

您可以執行以下操作:

template <int... Is>
using u_list = std::integer_sequence<int, Is...>;

template <char... Cs>
using c_list = std::integer_sequence<char, Cs...>;

template<int, char> struct foo {};

template<class ...> struct bar {};

template <std::size_t I, typename T, template <typename, T...> class C, T ... Is>
constexpr T get(C<T, Is...> c)
{
    constexpr T values[] = {Is...};
    return values[I];
}


template <std::size_t I, typename T>
constexpr auto get_v = get<I>(T{});


template<int... Ints, char ... Chars, std::size_t ... Is>
auto cartesian_product(u_list<Ints...>, c_list<Chars...>, std::index_sequence<Is...>)
-> bar<foo<
        get_v<Is / sizeof...(Chars), u_list<Ints...> >,
        get_v<Is % sizeof...(Chars), c_list<Chars...> >
        >...
    >;

template<int... Ints, char ... Chars>
auto cartesian_product(u_list<Ints...> u, c_list<Chars...> c)
-> decltype(cartesian_product(u, c, std::make_index_sequence<sizeof...(Ints) * sizeof...(Chars)>()));




using int_vals = u_list<1, 5, 7>;
using char_vals = c_list<-3, 3>;

using result_t = decltype(cartesian_product(int_vals{}, char_vals{}));

演示版

標准部分的可能實現:

template <typename T, T ... Is> struct integer_sequence{};

template <std::size_t ... Is>
using index_sequence = integer_sequence<std::size_t, Is...>;

template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};

template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};

並改變答案:

template <std::size_t I, typename T, template <typename, T...> class C, T ... Is>
constexpr T get(C<T, Is...> c)
{
    using array = T[];
    return array{Is...}[I];
}

template<int... Ints, char ... Chars, std::size_t ... Is>
auto cartesian_product(u_list<Ints...>, c_list<Chars...>, index_sequence<Is...>)
-> bar<foo<
        get<Is / sizeof...(Chars)>(u_list<Ints...>{}),
        get<Is % sizeof...(Chars)>(c_list<Chars...>{})
        >...
    >;

演示C ++ 11

在我看來,在純類型領域中進行模板元編程要容易得多。

從非類型模板參數范圍轉移到類型范圍,然后再返回,這需要一些工作,但這意味着您正在使用通用元編程實用程序,而不是針對您的問題的通用元編程實用程序。


因此,我將您的問題簡化為一系列類型上的笛卡爾積。

這是我的打字包:

template<class...Ts>struct types {
  using type=types; // makes inheriting from it useful
  static constexpr std::size_t size = sizeof...(Ts);
};

首先我們編寫fmap Fmap接受一個函數和一個列表,並返回應用了該函數的列表中每個元素的列表。

template<template<class...>class Z, class List>
struct fmap {};
template<template<class...>class Z, class List>
using fmap_t = typename fmap<Z,List>::type;
template<template<class...>class Z, class...Ts>
struct fmap<Z, types<Ts...>>:
  types<Z<Ts>...>
{};

fapply fapply也接受一個函數和一個列表,但是將該函數應用於整個列表元素集。

template<template<class...>class Z, class List>
struct fapply {};
template<template<class...>class Z, class List>
using fapply_t=typename fapply<Z,List>::type;
template<template<class...>class Z, class...Ts>
struct fapply<Z, types<Ts...>> {
  using type=Z<Ts...>;
};

碰巧的是,部分應用fapply非常有用:

template<template<class...>class Z>
struct applier {
    template<class List>
    using apply = fapply_t<Z,List>;
};

我們希望能夠合並列表:

template<class...>
struct cat:types<> {};
template<class...As, class...Bs, class...Cs>
struct cat<types<As...>, types<Bs...>, Cs...>:
    cat<types<As..., Bs...>, Cs...>
{};
template<class...As>
struct cat<types<As...>>:types<As...>{};
template<class...Ts>using cat_t=typename cat<Ts...>::type;

然后,這里是cart_product_t:

template<class A, class B>
struct cart_product {};
template<class A, class B>
using cart_product_t = typename cart_product<A,B>::type;
template<class A, class... Bs>
struct cart_product<types<A>, types<Bs...>>:
  types< types<A, Bs>... >
{};
// reduce cart_product to cart_product on a one element list on the lhs:
template<class...As, class... Bs>
struct cart_product<types<As...>, types<Bs...>>:
  fapply_t<
    cat_t,
    fmap_t<
      applier<cart_product_t>::template apply,
      types<
        types< types<As>, types<Bs...> >...
      >
    >
  >
{};

特定於您的問題的類型:

template<int...>struct u_list {};
template<char...>struct c_list {};
template<int, char>struct foo {};
template<class...>struct bar{};

一種將值列表提升為類型的工具:

template<class> struct lift {};
template<int...is> struct lift<u_list<is...>>:
  types< std::integral_constant<int, is>... >
{};
template<char...is> struct lift<c_list<is...>>:
  types< std::integral_constant<char, is>... >
{};
template<class T>using lift_t=typename lift<T>::type;

lower_to_foo采用一對類型,並將它們轉換為foo:

template<class I, class C>
using lower_to_foo = foo<I::value, C::value>;

現在我們將它們放在一起:

using int_vals = u_list<1, 5, 7>;
using char_vals = c_list<-3, 3>;

using product = cart_product_t< lift_t<int_vals>, lift_t<char_vals> >;
static_assert( product::size == 6, "should be 6" );
using result_t = fapply_t< bar, fmap_t< applier<lower_to_foo>::template apply, product > >;

using ref_t = bar<
  foo<1, -3>, foo<1, 3>,
  foo<5, -3>, foo<5, 3>,
  foo<7, -3>, foo<7, 3>
>;
ref_t test = result_t{}; // gives better error messages than static_assert
static_assert(std::is_same<result_t, ref_t >::value, "");

鮑勃是你叔叔

catfmapfapply在函數式編程中都是相對標准的函數。 applier僅允許您編寫使用元素而不是列表的模板映射函數(這是部分應用的fapply )。

現場例子


現在,還記得我怎么說模板元編程對類型更容易嗎?

注意所有那些模板模板參數嗎? 如果它們是類型,它將變得更加容易。

template<template<class...>class Z>
struct ztemplate {
  template<class...Ts>using apply=Z<Ts...>;
};

您可以使用ztemplate上的類型標簽和operator()進行ztemplate風格的constexpr元編程,這很有趣。

以類型列表交叉產品為基礎

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

template<int ...> struct u_list {};

template<char ...> struct c_list {};

template<int, char > struct foo {};

template<typename...> struct type_list {};

我們用row擴展char...

template<int I, char... Cs>
  struct row
{
  typedef type_list<foo<I,Cs>...> type;
};

template <typename... T> struct concat;

template <typename... S, typename... T>
struct concat<type_list<S...>, type_list<T...>>
{
    using type = type_list<S..., T...>;
};

我們想要concat的額外專業化突破基本案例

template <typename... T>
struct concat<type_list<T...>, void>
{
    using type = type_list<T...>;
};

template<typename I, typename C>
struct cross_product;

基本情況:沒有更多的整數

template<char... Cs>
struct cross_product<u_list<>, c_list<Cs...>>
{
    using type = void;
};

遞歸的情況:一個int,然后是一包int

template<int I, int... Is, char... Cs>
struct cross_product<u_list<I, Is...>, c_list<Cs...>>
{
    using type = typename concat<typename row<I,Cs...>::type, typename cross_product<u_list<Is...>, c_list<Cs...>>::type>::type;

};

int main()
{
  using int_vals = u_list<1, 5, 7>;
  using char_vals = c_list<-3, 3>;

  using result_t = cross_product<int_vals, char_vals>::type;
  using ref_t = type_list<
      foo<1, -3>, foo<1, 3>,
      foo<5, -3>, foo<5, 3>,
      foo<7, -3>, foo<7, 3>
  >;

  static_assert(std::is_same<result_t, ref_t >::value, "");
  return 0;
}

住在科利魯!

以下是我的2美分...

如果您想要一個通用的解決方案,我看到的更大的問題是,從int_valschar_vals類型中char_vals並不容易(在C ++ 11中;在C ++ 17中更簡單)提取包含值的類型( intchar )。

因此,我想您必須將它們與foobar一起傳遞給magic<> (如果您不希望對foobar硬編碼)。

所以對magic<>的調用變成了(以我的方式)

using result_t
   = typename magic<int, char, foo, bar, int_vals, char_vals>::type; 

以下是我的解決方案的完整示例。

#include <type_traits>

template <int...>  struct u_list {};
template <char...> struct c_list {};

template <int, char>    struct foo {};
template <typename ...> struct bar {};

template <typename T1, typename T2, T1 t1, T2 ... T2s>
struct midProd
 { };

template <typename T1, typename T2, template <T1, T2> class, typename...>
struct magicHelper;

template <typename T1, typename T2,
          template <T1, T2> class ResIn,
          template <typename...> class ResOut,
          typename ... R>
struct magicHelper<T1, T2, ResIn, ResOut<R...>>
 { using type = ResOut<R...>; };

template <typename T1, typename T2,
          template <T1, T2> class ResIn,
          template <typename...> class ResOut,
          typename ... R, T1 ts1, T2 ... ts2, typename ... MpS>
struct magicHelper<T1, T2, ResIn, ResOut<R...>,
              midProd<T1, T2, ts1, ts2...>, MpS...>
 { using type = typename magicHelper<T1, T2, ResIn,
                   ResOut<R..., ResIn<ts1, ts2>...>, MpS...>::type; };


template <typename T1, typename T2,
          template <T1, T2> class,
          template <typename...> class,
          typename, typename>
struct magic;

template <typename T1, typename T2,
          template <T1, T2> class ResIn,
          template <typename...> class ResOut,
          template <T1...> class C1, template <T2...> class C2,
          T1 ... ts1, T2 ... ts2>
struct magic<T1, T2, ResIn, ResOut, C1<ts1...>, C2<ts2...>>
 { using type = typename magicHelper<T1, T2, ResIn, ResOut<>,
                   midProd<T1, T2, ts1, ts2...>...>::type ; };

int main ()
 {
   using int_vals  = u_list<1, 5, 7>;
   using char_vals = c_list<-3, 3>;

   using result_t
      = typename magic<int, char, foo, bar, int_vals, char_vals>::type;

   using ref_t = bar< foo<1, -3>, foo<1, 3>,
                      foo<5, -3>, foo<5, 3>,
                      foo<7, -3>, foo<7, 3> >;

   static_assert(std::is_same<result_t, ref_t >::value, "");
 }

顯然,如果您希望對某些類型( u_listc_listfoobar )進行硬編碼,則解決方案將變得更加簡單

#include <type_traits>

template <int...>  struct u_list {};
template <char...> struct c_list {};

template <int, char>    struct foo {};
template <typename ...> struct bar {};

template <int, char...> struct midProd {};

template <typename...>
struct magicH;

template <typename ... R>
struct magicH<bar<R...>>
 { using type = bar<R...>; };

template <typename ... R, int i, char ... cs, typename ... MpS>
struct magicH<bar<R...>, midProd<i, cs...>, MpS...>
 { using type = typename magicH<bar<R..., foo<i, cs>...>, MpS...>::type; };


template <typename, typename>
struct magic;

template <int ... is, char ... cs>
struct magic<u_list<is...>, c_list<cs...>>
 { using type = typename magicH<bar<>, midProd<is, cs...>...>::type; };

int main ()
 {
   using int_vals  = u_list<1, 5, 7>;
   using char_vals = c_list<-3, 3>;

   using result_t = typename magic<int_vals, char_vals>::type;

   using ref_t = bar< foo<1, -3>, foo<1, 3>,
                      foo<5, -3>, foo<5, 3>,
                      foo<7, -3>, foo<7, 3> >;

   static_assert(std::is_same<result_t, ref_t >::value, "");
 }

與C ++ 17中的其他代碼相同:

// Type your code here, or load an example.
#include <type_traits>

template<int ...>
struct u_list {};

template<char ...>
struct c_list {};

template<int, char >
struct foo {};

template<class ...>
struct bar {};

using int_vals = u_list<1, 5, 7>;
using char_vals = c_list<-3, 3>;

template<class... Args> struct type_list{
    template<class> struct make_concat;
    template<class ...Xs>
    struct make_concat<type_list<Xs...>>{
        using type = type_list<Args...,Xs...>;
    };
    template<class T>
    using concat = typename make_concat<T>::type;
    template<template<class...>class TT>
    using applied_to = TT<Args...>;
};

template<
         template<auto,auto> class C
        ,class X,class Y,class Yit=Y>
struct cart_prod;
template<template<auto,auto> class C,
         template<auto...> class Xt,
         template<auto...> class Yt,
         class Yit,
         auto Xi,auto...Xis,auto Yi,auto...Yis>
struct cart_prod<C,Xt<Xi,Xis...>,Yt<Yi,Yis...>,Yit>{
    using type = typename type_list<class C<Xi,Yi>>
       ::template concat<typename cart_prod<C,Xt<Xi,Xis...>,Yt<Yis...>,Yit>::type>;
};
template<template<auto,auto> class C,
         template<auto...> class Xt,
         template<auto...> class Yt,
         class Yit,
         auto Xi,auto...Xis,auto Yi>
struct cart_prod<C,Xt<Xi,Xis...>,Yt<Yi>,Yit>{
    using type = typename type_list<class C<Xi,Yi>>
       ::template concat<typename cart_prod<C,Xt<Xis...>,Yit,Yit>::type>;
};
template<template<auto,auto> class C,
         template<auto...> class Xt,
         template<auto...> class Yt,
         class Yit,
         auto Xi,auto Yi>
struct cart_prod<C,Xt<Xi>,Yt<Yi>,Yit>{
    using type = type_list<class C<Xi,Yi>>;
};


using result_t = cart_prod<foo,int_vals,char_vals>::type::applied_to<bar>;
using ref_t = bar<
    foo<1, -3>, foo<1, 3>,
    foo<5, -3>, foo<5, 3>,
    foo<7, -3>, foo<7, 3>
>;

static_assert(std::is_same<result_t, ref_t >::value, "");

另一個(但更短)的解決方案可能是

template<typename Ret,typename R>
auto magic( bar<u_list<>, R>, Ret result, R ) { return result; }

template<int I, int... Ints, typename... Foos, typename R>
auto magic( bar<u_list<I,Ints...>, c_list<>>, bar<Foos...>, R rollback ) { return magic(
    bar<u_list<Ints...>,R>{}, bar<Foos...>{}, rollback );}

template<int I, int... Ints, char J, char ... Chars, typename... Foos, typename R >
auto magic( bar<u_list<I,Ints...>, c_list<J,Chars...>>, bar<Foos...>, R rollback ) { return magic(
    bar<u_list<I,Ints...>, c_list<Chars...>>{},
    bar<Foos...,foo<I,J>>{},
    rollback );}

using result_t = decltype(magic( bar<int_vals,char_vals>{}, bar<>{}, char_vals{} ));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM