在编译时对类型的置换P(N,R)
Permutation P(N,R) of types in compile time
我已经编写了一个工作代码,用于在包具有所有不同类型时计算包的p(N,R),例如
PermutationN<2, P<int, char, bool>>
是
P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
但是当有重复的元素时,我得到的结果是错误的。例如,
PermutationN<2, P<int, int, char>>
应该是
P< P<int, int>, P<int, char>, P<char, int> >
这是我在所有类型不同时的工作代码。我一直纠结于如何调整它,以便在包中有重复类型时能给出正确的结果。如有任何帮助,我们将不胜感激。
#include <iostream>
#include <type_traits>
template <typename, typename> struct Merge;
template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;
template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
// P<Prev..., Rest...> are the remaining elements, thus ensuring that the next
// element chosen will not be First. The new Prev... is empty since we now start
// at the first element of P<Prev..., Rest...>.
typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type,
// Using P<Rest...> ensures that the next set of permutations will begin with the
// type after First, and thus the new Prev... is Prev..., First.
typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type
> {};
template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
using type = P<>;
};
template <template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<0, P<First, Rest...>, P<Prev...>, Output...> {
using type = P<P<Output...>>;
};
template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
using type = P<P<Output...>>;
};
template <typename Pack> struct EmptyPack;
template <template <typename...> class P, typename... Ts>
struct EmptyPack<P<Ts...>> { using type = P<>; };
template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;
// Testing
template <typename...> struct P;
int main() {
std::cout << std::is_same<
PermutationN<2, P<int, char, bool>>,
P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
>::value << 'n'; // true
std::cout << std::is_same<
PermutationN<2, P<int, int, int>>,
P< P<int, int>, P<int, int>, P<int, int>, P<int, int>, P<int, int>, P<int, int> >
>::value << 'n'; // true (but the answer should be P< P<int, int> >.
}
注意:我正在寻找一个优雅(高效)的解决方案,它不是简单地执行上述操作,然后只从输出中删除所有重复包(我已经可以做到了,但拒绝写这样一个丑陋、低效的解决方案来解决问题的核心),而是直接获得正确的输出。这就是我被卡住的地方。
基本思想是将最初的类型列表处理为(type, count)
对的列表,然后从那里开始工作。首先,一些基元:
template<class, size_t> struct counted_type {};
template<class...> struct pack {};
我们的表示将是counted_type
s的pack
。要构建它,我们需要能够向它添加一个类型:
template<class T, class CT> struct do_push;
template<class T, class...Ts, size_t... Is>
struct do_push<T, pack<counted_type<Ts, Is>...>>{
using type = std::conditional_t<std::disjunction_v<std::is_same<Ts, T>...>,
pack<counted_type<Ts, Is + (std::is_same_v<Ts, T>? 1 : 0)>...>,
pack<counted_type<Ts, Is>..., counted_type<T, 1>>
>;
};
template<class T, class CT> using push = typename do_push<T, CT>::type;
如果类型已经存在,我们将计数加1;否则我们附加一个CCD_ 4。
为了以后使用它,我们需要能够从中删除一种类型:
template<class T, class CT> struct do_pop;
template<class T, class...Ts, std::size_t... Is>
struct do_pop<T, pack<counted_type<Ts, Is>...>>{
using type = remove<counted_type<T, 0>,
pack<counted_type<Ts, Is - (std::is_same_v<Ts, T>? 1 : 0)>...>>;
};
template<class T, class CT> using pop = typename do_pop<T, CT>::type;
remove<T, pack<Ts...>>
从Ts...
中删除T
的第一个实例(如果它存在),并返回生成的包(如果T
不存在,则返回的包不变)。(相当无聊的)实现留给读者练习。
使用push
,我们可以很容易地从以下类型的pack
构建counted_type
s的pack
:
template<class P, class CT = pack<> >
struct count_types_imp { using type = CT; };
template<class CT, class T, class... Ts>
struct count_types_imp<pack<T, Ts...>, CT>
: count_types_imp<pack<Ts...>, push<T, CT>> {};
template<class P>
using count_types = typename count_types_imp<P>::type;
现在,实际实现是
template<class T> struct identity { using type = T; };
template <std::size_t N, typename CT, typename = pack<> > struct PermutationNHelper;
// Workaround for GCC's partial ordering failure
template <std::size_t N, class CT, class> struct PermutationNHelper1;
template <std::size_t N, class... Types, std::size_t... Counts, class... Current >
struct PermutationNHelper1<N, pack<counted_type<Types, Counts>...>, pack<Current...>> {
// The next item can be anything in Types...
// We append it to Current... and pop it from the list of types, then
// recursively generate the remaining items
// Do this for every type in Types..., and concatenate the result.
using type = concat<
typename PermutationNHelper<N-1, pop<Types, pack<counted_type<Types, Counts>...>>,
pack<Current..., Types>>::type...
>;
};
template <std::size_t N, class... Types, std::size_t... Counts, class... Current >
struct PermutationNHelper<N, pack<counted_type<Types, Counts>...>, pack<Current...>> {
using type = typename std::conditional_t<
N == 0,
identity<pack<pack<Current...>>>,
PermutationNHelper1<N, pack<counted_type<Types, Counts>...>,
pack<Current...>>
>::type;
// Note that we don't attempt to evaluate PermutationNHelper1<...>::type
// until we are sure that N > 0
};
template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, count_types<Pack>>::type;
通常情况下,这可以在一个具有两个部分专门化的模板中完成(一个用于N>0,一个用于N==0),但GCC的部分排序有缺陷,所以我使用conditional
显式调度。事实上,在N
等于0的情况下评估PermutationNHelper1
中的包扩展会发生可怕的爆炸,因此引入了缺乏想象力的PermutationNHelper1
,以提供额外的间接性水平并防止爆炸。
concat
只是您的Merge
(好吧,typename Merge<...>::type
)的可变版本。实现留给读者练习。
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 使用简单类型列表实现的指数编译时间.为什么
- 编译标准库类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 非类型指针和引用模板参数,以及在编译时如何/为什么解析它们.c++
- 使用 make 编译 MPI,几个命名空间错误,例如"错误:未知类型名称'使用'?
- C++ 编译错误:意外的类型名称"字符串":预期的表达式
- 为什么 std::make_shared 无法编译带有已删除运算符 new 的类型?
- 标量类型的特征模板无法编译固定大小的子矩阵操作
- 在其他容器中使用 boost::container::static_vector 时,GCC 编译错误"将'const s'绑定到类型's&'的引用丢弃限定符"
- 由于类型从 C 转换为 C++,无法编译错误 C2440
- 通过编译时值推断整数的类型
- 基于派生类型的编译时行为分支
- C++ 编译错误:gnu_printf是无法识别的格式函数类型
- 将函数类型作为模板参数传递不会编译
- 编译类型的时静态数组
- 警告LNK4206:找不到预编译类型信息; 未链接或覆盖;链接对象,就好像没有调试信息一样
- GCC 编译类型错误:预期的类型说明符
- c++编译类型资源所有者断言
- 基于字符串的类型层次结构,带有编译类型检查