当包装具有所有不同类型时,我已经为包计算了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> >.
}
N.B。我正在寻找一种优雅(高效)的解决方案,它不仅仅是执行上述操作,而只是从输出中删除所有重复包(我已经可以这样做但拒绝写出这样一个丑陋,低效的解决方案,不解决问题的核心),而是直接得到正确的输出。这就是我被困的地方。
答案 0 :(得分:1)
基本思想是将初始类型列表处理为(type, count)
对列表,并从那里开始工作。首先,一些原语:
template<class, size_t> struct counted_type {};
template<class...> struct pack {};
我们的代表将是pack
counted_type
。要构建它,我们需要能够为它添加一个类型:
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添加到计数中;否则我们追加counted_type<T, 1>
。
稍后使用它,我们需要能够从中删除一个类型:
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...>>
从T
移除Ts...
的第一个实例(如果存在),并返回生成的包(如果T
不存在,则为没有变化)。 (相当无聊的)实现留给读者练习。
使用push
,我们可以轻松地从pack
类型构建counted_type
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的部分排序是错误的,所以我用{{1明确调度}}。实际上评估conditional
中PermutationNHelper1
等于0的包扩展将会爆炸性地爆发,因此引入了缺乏想象力的N
以提供额外的间接级别并防止爆炸。
PermutationNHelper1
只是concat
的一个可变版本(嗯,Merge
)。实施留给读者练习。