为了好玩,我尝试使用可变参数模板 std::integer_sequence 和 std::tuple_cat手动实现这项工作,实际上很惊讶它可以很容易地工作:基于简单的任意长度的数组
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
并给定可变模板类(例如Foo 和Bar),它自己生成所有可能的排列并将它们合并到std::tuple(或boost::mpl::list)数据类型可以定义一个方便的别名:
using SomeAlias = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
下一节将详细介绍如何实现这一点!
为此,我编写了一个类 - 基于一些模板参数,例如上述 int 和 char 参数的组合以及相应的 template template class - 创建一个包含 此单个template template class 的int 和char 数组的所有排列。这是通过创建两个包含成对排列的排列向量来完成的。例如。两个输入数组t1_in = {1, 2} 和t2_in = {'a', 'b', 'c'}; 使用函数duplicateArray 扩展为t1 = {1, 1, 1, 2, 2, 2} 和t2 = {'a', 'b', 'c', 'a', 'b', 'c'},然后创建一个std::integer_sequence,以便将两者融合到一个模板T<t1[I], t2[I]> 中std::integer_sequence 为您提供单个类的所有排列:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1_in, std::array<char,I2> const& t2_in, template <int, char> class T>
class SingleClassAllPermutations {
private:
template <std::size_t K, std::size_t I, typename TA, std::size_t J>
static constexpr auto duplicateArray(std::array<TA, J> const& arr_in) {
std::array<TA, I*J*K> arr_out {0};
std::size_t l {0};
for (std::size_t i = 0; i < I; ++i) {
for (std::size_t j = 0; j < J; ++j) {
for (std::size_t k = 0; k < K; ++k) {
arr_out[l] = arr_in[j];
++l;
}
}
}
return arr_out;
}
static constexpr std::size_t N = I1*I2;
static constexpr std::array<int,N> t1 = duplicateArray<I2,1>(t1_in);
static constexpr std::array<char,N> t2 = duplicateArray<1,I1>(t2_in);
static constexpr auto index_seq = std::make_index_sequence<N>{};
template <std::size_t... I>
static constexpr auto getTuple(std::index_sequence<I...>) {
return std::make_tuple(T<t1[I], t2[I]>() ...);
}
public:
static constexpr auto tup = getTuple(index_seq);
};
然后我创建了一个可变参数模板,它可以采用几个不同的template template classes(只要它们的模板参数分别是int 和char)作为附加输入参数,然后合并内部元组tup 与std::tuple_cat 的各个排列创建元组包含int 和char 数组的所有可能排列 以及不同的可变参数template template classes: p>
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1, std::array<char,I2> const& t2, template <int,char> class... Ts>
class AllClassesAllPermutations {
public:
static constexpr auto getTuple() {
return std::tuple_cat(SingleClassAllPermutations<I1,I2,t1,t2,Ts>::tup ...);
}
using type = decltype(getTuple());
};
现在可以在命名空间内定义全局constexpr 数组和方便的alias,例如
namespace some_namespace {
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
using SomeInstantiation = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
}
这样可以重复使用上面的模板来生成不同的数据类型。
模板然后将其扩展为所有可能的排列,在上述情况下为
std::tuple<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
最后,如果你想有一个boost::mpl::list,你可以引入一个转换函数,比如
template <class... Ts>
static constexpr auto tupleToMplList(std::tuple<Ts...>) {
return boost::mpl::list<Ts...>{};
}
你再次使用 decltype 而不是 std::tuple 导致数据类型
boost::mpl::list<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
Try it here