5

比方說,我有創建兩個可變參數,非類型模板參數的笛卡爾積擴展包

  • 非類型模板parameteres的兩個列表(其中可能有不同的類型)
  • 模板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的/make_index_sequence

迄今爲止我發現的最近的是:How to create the Cartesian product of a type list?。所以原則上(我還沒有測試過)應該可以將非參數包變成類型參數包,然後在鏈接的帖子中應用解決方案,但是我希望沿着這個方向有一個更簡單/更短的解決方案這樣的行:

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{})); 
+0

我一直在做一些討厭的東西與參數組合...也許這可能會有所幫助:[\ [點擊我\]](https://stackoverflow.com/questions/39687907/is-it-possible -to-invoke-a-all-possible-k-combinations-with-repetit) –

回答

2

你可以做一些類似如下:

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{})); 

Demo

可能實現的std部分:

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...>{}) 
     >... 
    >; 

Demo C++11

+0

謝謝。乍一看,這似乎是解決方案,這對我來說是最易讀的。我將不得不嘗試一下真正的代碼,看看最適合我的答案。 – MikeMB

+0

你可以把它轉換成C++ 11解決方案嗎? – MikeMB

+0

@MikeMB:添加了C++ 11版本。 – Jarod42

2

在我看來,在純類型的域中做模板元編程是非常容易的。

需要一些工作從非類型模板參數的土地移動到類型的土地,然後再回來,但這意味着您正在使用通用的元編程實用程序而不是特定於您的問題的實用程序。


所以,我會減少你的問題到一個類型列表笛卡爾產品。

這是我喜歡的類型包:

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>...> 
{}; 

and 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>; 
}; 

我們會希望能夠concatinate名單:

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, ""); 

和鮑勃是你的叔叔。

cat,fmapfapply都是功能性編程中相對標準的功能。 applier只是讓你寫你的模板映射功能,而不是列表(這是部分應用fapply)。

Live example


現在,請記住我如何說模板元編程更容易使用類型?

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

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

,你可以向下走一路花風格的元編程constexpr型標籤和operator()ztemplate和其他樂趣。

+0

std :: integral_constant實際上是否必須具有整數類型,或者是例如類枚舉也可能嗎? – MikeMB

+0

@MikeMB Mate,甚至可以使用函數指針。他們甚至可以在C++ 14中調用。 – Yakk

+0

謝謝。這似乎是一個很好的解決方案,但除非我們的代碼中需要更多的tmp,否則我有點不情願爲我的單個用例添加如此多的通用工具。 – MikeMB

0

以類型列表跨產品作爲基礎

#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

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額外的專業化拓展char...包打出來的基本情況

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,其次是整數

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; 
} 
的包

Live on Coliru!

0

以下是我的2美分...

如果你想有一個通用的解決方案,我看到的更大的問題是,從int_valschar_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, ""); 
} 
0

別人一樣在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, ""); 
0

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

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{}));