我遇到了以下问题:
给定由Node<>类型的非终端节点表示的树.和A,B等任意类型的终端节点(见下文).
因为我不想使用runtime-polymorphism,所以我喜欢通过constexpr函数将树转换为std :: tuple,就像下面示例中的立即调用的lambda表达式一样.
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
template<typename... T>
struct Node {
constexpr Node(const T&... n) : mChildren{n...} {}
std::tuple<T...> mChildren;
};
template<uint8_t N>
struct IndexNode {
std::array<uint8_t, N> mChildren;
};
int main() {
constexpr auto tree = []() {
auto t = Node(A(),
B(),
Node(C(),
Node(D())),
E());
// transform t into std::tuple<A, B, C, D, IndexNode<1>{3}, IndexNode<2>{2, 4}, E, IndexNode<4>{0, 1, 5, 6}>
// return ...;
}();
}
我们的想法是使用元组元素的索引作为树的活动(选定)节点的“指针”.总体目的是在μC上实现菜单系统,而不使用运行时多态性.
如果我可以在编译时执行这个转换,我可以使用一个特殊的元函数来检索活动的元组元素并在其上调用一些函数.我已经写过这个功能了.
缺少的链接肯定会是某种深度优先的树遍历……但我无法弄明白.
最佳答案 如何使用大量的std :: tuple_cat,std :: index_sequence和递归如下?
#include <tuple>
#include <array>
#include <iostream>
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
template <typename... T>
struct Node
{
constexpr Node (T const & ... n) : mChildren { n... }
{ }
std::tuple<T...> mChildren;
};
template <std::size_t N>
struct IndexNode
{ std::array<uint8_t, N> mChildren; };
template <typename>
struct cntT : public std::integral_constant<std::size_t, 1U>
{ };
template <typename ... Ts>
struct cntT<Node<Ts...>>
: public std::integral_constant<std::size_t, 1U + (cntT<Ts>::value + ...)>
{ };
template <typename T>
struct getT
{
constexpr auto operator() (T const & t, std::size_t & cnt)
{ ++cnt; return std::make_tuple(t); }
};
template <typename ... Ts>
struct getT<Node<Ts...>>
{
template <std::size_t ... Is>
constexpr auto func (std::tuple<Ts...> const & t,
std::index_sequence<Is...> const &,
std::size_t & cnt)
{
std::size_t val { cnt };
IndexNode<sizeof...(Ts)> in
{ { { uint8_t(val += cntT<Ts>::value)... } } };
return std::tuple_cat(getT<Ts>()(std::get<Is>(t), cnt)...,
std::make_tuple(in));
}
constexpr auto operator() (Node<Ts...> const & n, std::size_t & cnt)
{
return func(n.mChildren, std::make_index_sequence<sizeof...(Ts)>{},
cnt);
}
};
template <typename ... Ts>
constexpr auto linearNode (Node<Ts...> const & n)
{
std::size_t cnt ( -1 );
return getT<Node<Ts...>>()(n, cnt);
}
int main()
{
constexpr auto tree = []()
{
auto t = Node { A{}, B{}, Node{ C{}, Node{ D{} } }, E{} };
return linearNode(t);
}();
static_assert( std::is_same<
decltype(tree),
std::tuple<A, B, C, D, IndexNode<1>, IndexNode<2>, E,
IndexNode<4>> const>::value, "!");
std::cout << "IndexNode<1> { ";
for ( auto const & v : std::get<4U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<1> { 3, }
std::cout << "IndexNode<2> { ";
for ( auto const & v : std::get<5U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<2> { 2, 4, }
std::cout << "IndexNode<4> { ";
for ( auto const & v : std::get<7U>(tree).mChildren )
std::cout << int(v) << ", ";
std::cout << "}" << std::endl; // print IndexNode<4> { 0, 1, 5, 6, }
}