您可以这样做,但一般的解决方案可能比直接输入要长。
我们想将枚举插入到模板参数中并得到一个组合列表:
using Input1 = Sequence<Type1_1, Type1_2>;
using Input2 = Sequence<Type2_1, Type2_2>;
using Input3 = Sequence<Type3_1, Type3_2>;
using Output = Combine<Input1, Input2, Input3>;
/* result: CombinationSet<
Combination<Type1_1, Type2_1, Type3_1>,
Combination<Type1_1, Type2_1, Type3_2>,
Combination<Type1_1, Type2_2, Type3_1>,
// etc
*/
所以,首先,定义输入和输出类型:
template <typename T, T... Cs>
struct Sequence {
using Type = T; //
};
// represents one iteration of Combined lists
template <typename, typename, typename>
struct Combination;
// a set of Combinations
template <typename... Ts>
struct CombinationSet;
现在,可以定义第一级:将两个常量和一个序列转换为一组组合。我们可以通过偏特化推导出常量和序列的类型:
template <typename, typename, typename>
struct CombineImpl;
template <typename T1, typename T2, typename T3, T1 C1, T2 C2, T3... C3s>
struct CombineImpl<std::integral_constant<T1, C1>, std::integral_constant<T2, C2>, Sequence<T3, C3s...>>
{
using Type = CombinationSet<Combination<std::integral_constant<T1, C1>, std::integral_constant<T2, C2>, std::integral_constant<T3, C3s>>...>;
};
/* example:
CombineImpl<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
Sequence<Type3, Type3_1, Type3_2>
>
gets turned into:
CombinationSet<
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_1>
>,
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_2>
>
>
*/
现在下一级:如果我们有一个常量和两个序列,我们可以多次调用CombineImpl<C, C, S>,得到一堆CombinationSets。但是,我们需要一种连接所有集合的方法:
template <typename T1, typename T2, typename T3, T1 I1, T2... I2s, T3... I3s>
struct CombineImpl<std::integral_constant<T1, I1>, Sequence<T2, I2s...>, Sequence<T3, I3s...>>
{
using Type = Merge<typename CombineImpl<std::integral_constant<T1, I1>, std::integral_constant<T2, I2s>, Sequence<T3, I3s...>>::Type...>;
// ^^^^^ how to implement this?
};
我只能考虑使用递归类型来展平集合。可能有更好的log n 解决方案,但我不够聪明,无法做到:
// stub - the first type is the output set, followed by input sets
template <typename... Ts>
struct MergeImpl;
// recursive bit - deduce the combinations in the next set, add to output
template <typename... TOut, typename... Ts, typename... TIn>
struct MergeImpl<CombinationSet<TOut...>, CombinationSet<Ts...>, TIn...>
{
using Type = typename MergeImpl<CombinationSet<TOut..., Ts...>, TIn...>::Type;
};
// terminal - when there are no more inputs, expose the output.
template <typename... TOut>
struct MergeImpl<CombinationSet<TOut...>>
{
using Type = CombinationSet<TOut...>;
};
// type alias to start with an empty output set
template <typename... Ts>
using Merge = typename MergeImpl<CombinationSet<>, Ts...>::Type;
/* example:
Merge<
CombinationSet<
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_1>
>,
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_2>
>
>,
CombinationSet<
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_2>,
std::integral_constant<Type2, Type2_1>
>
>
>
Gets turned into:
CombinationSet<
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_1>
>,
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_1>,
std::integral_constant<Type2, Type2_2>
>
Combination<
std::integral_constant<Type1, Type1_1>,
std::integral_constant<Type2, Type2_2>,
std::integral_constant<Type2, Type4_1>
>
>
现在定义了合并,我们可以添加另一个层来获取三个序列的所有组合:
template <typename T1, typename T2, typename T3, T1... I1s, T2... I2s, T3... I3s>
struct CombineImpl<Sequence<T1, I1s...>, Sequence<T2, I2s...>, Sequence<T3, I3s...>>
{
using Type = Merge<typename CombineImpl<std::integral_constant<T1, I1s>, Sequence<T2, I2s...>, Sequence<T3, I3s...>>::Type...>;
};
template <typename T1, typename T2, typename T3>
using Combine = typename CombineImpl<T1, T2, T3>::Type;
下一步是从CombinationSet 创建地图。我将再次使用部分专业化:
// stub
template <typename>
struct CalculateHandlerImpl;
// specialization - deduce combination types and declare static map
template <typename T1, typename T2, typename T3, T1... C1s, T2... C2s, T3... C3s>
struct CalculateHandlerImpl<CombinationSet<Combination<std::integral_constant<T1, C1s>, std::integral_constant<T2, C2s>, std::integral_constant<T3, C3s>>...>> {
static std::map<std::tuple<T1, T2, T3>, std::function<int()>> value;
};
// static maps have to be defined out of line
template <typename T1, typename T2, typename T3, T1... C1s, T2... C2s, T3... C3s>
std::map<std::tuple<T1, T2, T3>, std::function<int()>> CalculateHandlerImpl<CombinationSet<Combination<std::integral_constant<T1, C1s>, std::integral_constant<T2, C2s>, std::integral_constant<T3, C3s>>...>>::value = {
{ std::make_tuple(C1s, C2s, C3s), Calculate<C1s, C2s, C3s> }...
};
最后,编写一个处理函数将它们绑定在一起:
template <typename T1, typename T2, typename T3>
int CalculateHandler(typename T1::Type t1, typename T2::Type t2, typename T3::Type t3) {
return CalculateHandlerImpl<Combine<T1, T2, T3>>::value[std::make_tuple(t1, t2, t3)]();
}
// maybe a variadic macro can help define this and the enum at the same time
using Type1Seq = Sequence<Type1,
kType1_1,
kType1_2,
kType1_3,
kType1_4,
kType1_5,
kType1_6>;
using Type2Seq = Sequence<
Type2,
kType2_1,
kType2_2,
kType2_3>;
using Type3Seq = Sequence<Type3,
kType3_1,
kType3_2,
kType3_3,
kType3_4>;
int main() {
CalculateHandler<Type1Seq, Type2Seq, Type3Seq>(kType1_1, kType2_2, kType3_1);
}
https://godbolt.org/z/WPaPqa4zT
现在,您会注意到所有这些模板代码可能至少有 72 行。另外,它肯定会增加编译时间。但如果列表增长,上述内容会有所帮助。并且它可以省去您检查是否包含所有排列的麻烦。