元组中类型的编译时重新排列
Compile-time rearrangement of types in a tuple
使用以下示例定义template <typename...> struct order;
的typedef:
order<A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>::type
是要
std::tuple<H,D,I,B,J,E,K,A,L,F,M,C,N,G,O>
因为原始类型是从左到右逐行排列在二叉树中的,如下图所示:
A
/
/
B C
/ /
D E F G
/ / / /
H I J K L M N O
在排序中,某种类型的剩余类型应位于该类型之前。 属于某种类型的类型在排序中应遵循该类型。所以我们可以看到 H 列在最前面,O 列在最后。 如前所述,生成的类型std::tuple<H,D,I,B,J,E,K,A,L,F,M,C,N,G,O>
。
如何在编译时构建此排序(对于元组的任何长度,即使最后一行未完成(? 任何帮助将不胜感激。
我的最新想法是使用递归:D,H,I
按H,D,I
排序。 E,J,K
按J,E,K
排序。 然后B,D,E,H,I,J,K
按H,D,I,
B,
J,E,K,
排序,即我们使用前两个排序来构建大一代的树的顺序,将"根"B 放在中间。 然后我们可以对 A 的右子树执行此操作,然后示例中的整个树可以类似地连接(中间为 A(。 类似的东西,但现在弄清楚如何将其转换为代码是问题所在。 大致如下(在改进和概括之前(:
template <typename... Packs> struct concat;
template <template <typename...> class P, typename... Ts, typename... Us>
struct concat<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <typename Pack1, typename Pack2, typename... Packs>
struct concat<Pack1, Pack2, Packs...> : concat<Pack1, typename concat<Pack2, Packs...>::type> {};
template <typename...> struct order;
template <typename T>
struct order<T> {
using type = std::tuple<T>;
};
template <typename A, typename B, typename C>
struct order<A,B,C> :
concat<typename order<B>::type, std::tuple<A>, typename order<C>::type> {};
template <typename A, typename B, typename C, typename D, typename E, typename F, typename G>
struct order<A,B,C,D,E,F,G> :
concat<typename order<B,D,E>::type, std::tuple<A>, typename order<C,F,G>::type> {};
连接三个std::index_sequence
s(概括起来很简单,但我在这里只需要三个(:
template<size_t... Seq1, size_t... Seq2, size_t... Seq3>
auto concat3_impl(std::index_sequence<Seq1...>,
std::index_sequence<Seq2...>,
std::index_sequence<Seq3...>)
-> std::index_sequence<Seq1..., Seq2..., Seq3...>;
template<class...Ts>
using concat3 = decltype(concat3_impl(Ts{}...));
级别顺序遍历为 0, 1, ..., (max - 1)
的完整二叉树的顺序遍历:
template<size_t start, size_t max, bool = (start < max) >
struct in_order;
template<size_t start, size_t max>
using in_order_t = typename in_order<start, max>::type;
template<size_t start, size_t max, bool >
struct in_order {
using type = concat3<in_order_t<2*start + 1, max>,
std::index_sequence<start>,
in_order_t<2*start + 2, max>>;
};
template<size_t start, size_t max >
struct in_order<start, max, false> {
using type = std::index_sequence<>;
};
根据索引列表对元组重新排序:
template<class Tuple, size_t...Is>
auto reorder_by_index_impl(std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, Tuple>...>;
template<class Tuple, class Index>
using reorder_by_index = decltype(reorder_by_index_impl<Tuple>(Index{}));
最后:
template<class Tuple>
using reorder_tuple = reorder_by_index<Tuple, in_order_t<0, std::tuple_size<Tuple>{}>>;
演示:
struct A{}; struct B{}; struct C{}; struct D{}; struct E{};
struct F{}; struct G{}; struct H{}; struct I{}; struct J{};
struct K{}; struct L{}; struct M{}; struct N{}; struct O{};
using t = reorder_tuple<std::tuple<A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>>;
using t = std::tuple<H,D,I,B,J,E,K,A,L,F,M,C,N,G,O>; // OK, same type.
这是 T.C. 的解决方案推广到 N 元树的任何类型的遍历,其中reorder<std::tuple, 2, 1, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>
是原始问题(二叉树顺序遍历(的特殊情况,因为 2 表示二叉树,1 表示与第一个子项递归后的操作。 2 和 1 可以采用任何其他值。
#include <type_traits>
#include <utility>
#include <tuple>
// Concatenating std::index_sequences.
template <typename... Packs> struct concat;
template <typename Pack>
struct concat<Pack> {
using type = Pack;
};
template <std::size_t... Is, std::size_t... Js>
struct concat<std::index_sequence<Is...>, std::index_sequence<Js...>> {
using type = std::index_sequence<Is..., Js...>;
};
template <typename Pack1, typename Pack2, typename... Packs>
struct concat<Pack1, Pack2, Packs...> : concat<Pack1, typename concat<Pack2, Packs...>::type> {};
// In-order traversal for a complete binary tree whose level-order traversal is 0,1,2, ..., max-1.
template <std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max, typename = void>
struct traversal {
using type = std::index_sequence<>; // So that concatenating changes nothing and ends the recursion.
};
// General recursion.
template <std::size_t Count, std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max, typename Pack, bool ActionJustTookPlace = false>
struct concat_traversals_impl : concat_traversals_impl<Count + 1, NumChildren, ActionPoint, Start, Max,
typename concat<Pack, typename traversal<NumChildren, ActionPoint, NumChildren * Start + Count + 1, Max>::type>::type> {};
// 0
// / |
// / |
// 1 2 3
// / |
// 4 5 6
// Above we see that the three children of node K is 3*K+1, 3*K+2, 3*K+3. In general, it is N*K+1, N*K+2, ..., N*K+N, where N is the number of children.
// If Count == ActionPoint (and ActionJustTookPlace == false), then concat the action, which is std::index_sequence<Start> in this case, but then let ActionJustTookPlace == true so that this does not happen infinitely (as Count still remains equal to ActionPoint) on the next template instantiation, and the primary template is used instead.
template <std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max, typename Pack>
struct concat_traversals_impl<ActionPoint, NumChildren, ActionPoint, Start, Max, Pack, false> : concat_traversals_impl<ActionPoint, NumChildren, ActionPoint, Start, Max,
typename concat<Pack, std::index_sequence<Start>>::type, true> {};
// End the recursion when Count == NumChildren.
template <std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max, typename Pack>
struct concat_traversals_impl<NumChildren, NumChildren, ActionPoint, Start, Max, Pack> {
using type = Pack;
};
// Special case of when Count == NumChildren and ActionPoint == NumChildren as well (this partial specialization is needed else there will be ambiguity compilng error).
template <std::size_t ActionPoint, std::size_t Start, std::size_t Max, typename Pack>
struct concat_traversals_impl<ActionPoint, ActionPoint, ActionPoint, Start, Max, Pack, false> : concat<Pack, std::index_sequence<Start>> {};
template <std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max>
using concat_traversals = typename concat_traversals_impl<0, NumChildren, ActionPoint, Start, Max, std::index_sequence<>>::type;
template <std::size_t NumChildren, std::size_t ActionPoint, std::size_t Start, std::size_t Max> // Recursive call.
struct traversal<NumChildren, ActionPoint, Start, Max, std::enable_if_t<(Start < Max)>> {
using type = concat_traversals<NumChildren, ActionPoint, Start, Max>;
};
// Now the actual reordering.
template <typename Pack, typename Sequence> struct reorder_helper;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct reorder_helper<P<Ts...>, std::index_sequence<Is...>> {
using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>;
};
template <template <typename...> class P, std::size_t NumChildren, std::size_t ActionPoint, typename... Ts>
using reorder = typename reorder_helper<P<Ts...>, typename traversal<NumChildren, ActionPoint, 0, sizeof...(Ts)>::type>::type;
// Special syntax for reordering a pack.
template <std::size_t NumChildren, std::size_t ActionPoint, typename Pack> struct reorder_pack;
template <std::size_t NumChildren, std::size_t ActionPoint, template <typename...> class P, typename... Ts>
struct reorder_pack<NumChildren, ActionPoint, P<Ts...>> {
using type = reorder<P, NumChildren, ActionPoint, Ts...>;
};
// Testing
struct A{}; struct B{}; struct C{}; struct D{}; struct E{}; struct F{}; struct G{}; struct H{};
struct I{}; struct J{}; struct K{}; struct L{}; struct M{}; struct N{}; struct O{};
int main() {
static_assert (std::is_same<
reorder<std::tuple, 2, 1, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>, // 2 means it is a binary tree, 1 means that we do left traversal, then the node action, then right traversal (i.e. inorder traversal).
std::tuple<H,D,I,B,J,E,K,A,L,F,M,C,N,G,O>
>::value, "");
static_assert (std::is_same<
reorder<std::tuple, 2, 0, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>, // 2 means it is a binary tree, 0 means that we do the node action, then left traversal, and then right traversal (i.e. preorder traversal).
std::tuple<A,B,D,H,I,E,J,K,C,F,L,M,G,N,O>
>::value, "");
static_assert (std::is_same<
reorder<std::tuple, 2, 2, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>, // 2 means it is a binary tree, 2 means that we do left traversal, then right traversal, then the node action (i.e. postorder traversal).
std::tuple<H,I,D,J,K,E,B,L,M,F,N,O,G,C,A>
>::value, "");
static_assert (std::is_same<
reorder_pack<3, 2, std::tuple<A,B,C,D,E,F,G,H,I,J,K,L,M,N,O>>::type, // 3 children per node. Do first child, second child, then node action, then do third child recursively.
std::tuple<N,O,E,F,B,G,H,I,C,J,A,K,L,D,M>
>::value, "");
}
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么不;名字在地图上是按顺序排列的吗
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 按对象的特定方法按升序排列的C++优先级队列
- 找到具有最多子串栅栏的字符串排列
- 重新排列单线以形成闭合多边形?
- 在数组中输入 n 个整数的列表,并以类似于钟摆来回移动的方式排列它们. 输入-1 3 2 5 4,输出5 3 1 2 4
- 输入的 C++ 排列
- 公共/私有/受保护是否会更改内存中结构的排列?
- 如何在 C/C++ 中生成具有 n 组 5 个值(重复项)的所有可能排列的矩阵
- 在向量C++中排列奇数和偶数
- 如何进行排列?
- 矩阵行求和与 RowMajor 和 ColMajor 数据排列的奇怪性能差异
- 在 CPP 中打印具有重复项的选定长度字符的所有排列
- 如何在不使用 C++ 中的数组或函数的情况下查找 N 位数字的所有排列
- 调用参数排列不变函数 f(i++, i++)
- 以 C++ 为单位具有输出限制的排列
- 在C++中寻找排列和组合
- 在 std::map 中重新排列键
- 为什么我得到长度仅为 3 的字符串排列的输出?