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