我试图表示一个树状的递归数据结构,其中每个节点可能是两种不同数据类型之一。我使用boost变量来“容纳”每个节点可能存在的两种类型。
然而,我遇到了一个问题。我正在使用'using'指令严格声明所有这些类型,所以当我到达节点的递归性质时,它会失败,因为typedef / using可能不会使用递归。
如何做到这一点?
using LeafData = int; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using InnerNode = std::unordered_map<std::string, boost_variant<InnerNode, LeafNode>>; // this is problematic since I'm using InnerNode recursively
我已经探索过使用boost :: make_recursive_variant,但是它创建的类型(A)并不是我需要的,因为它在一个变体中生成一个变体,而我想要一个由(内部节点)组成的(B)单个变体或LeafNode。
(A) boost_variant<boost_variant<InnerNode, LeafNode>, LeafNode>
(B) boost_variant<InnerNode, LeafNode>
答案 0 :(得分:1)
您可以使用make_recursive_variant
:
<强> Live On Coliru 强>
#include <boost/variant.hpp>
#include <unordered_map>
struct LeafData {
int _i;
LeafData(int i) : _i(i) {}
}; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using Node = boost::make_recursive_variant<
LeafNode,
std::unordered_map<std::string, boost::recursive_variant_>
>::type;
using Inner = std::unordered_map<std::string, Node>;
int main() {
Node tree = Inner {
{ "a", LeafNode { { "one", 1 }, { "two", 2 }, { "three",3 } } },
{ "b", Inner {
{ "b1", LeafNode { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
{ "b2", LeafNode { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
}
},
{ "c", LeafNode {} },
};
}
为什么要区分内部/叶子节点?在我看来,叶节点只是具有值而不是子节点的节点:
<强> Live On Coliru 强>
#include <boost/variant.hpp>
#include <unordered_map>
struct Data {
int _i;
Data(int i) : _i(i) {}
}; // just for illustration
using Tree = boost::make_recursive_variant<
Data,
std::unordered_map<std::string, boost::recursive_variant_>
>::type;
using Node = std::unordered_map<std::string, Tree>;
int main() {
Tree tree = Node {
{ "a", Node { { "one", 1 }, { "two", 2 }, { "three",3 } } },
{ "b", Node {
{ "b1", Node { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
{ "b2", Node { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
}
},
{ "c", Node {} },
};
}
你可以通过一个很好的判断前方声明:
<强> Live On Coliru 强>
#include <boost/variant.hpp>
#include <unordered_map>
struct Data {
int _i;
Data(int i) : _i(i) {}
}; // just for illustration
struct Node;
using Tree = boost::variant<Data, boost::recursive_wrapper<Node> >;
struct Node : std::unordered_map<std::string, Tree> {
using base = std::unordered_map<std::string, Tree>;
using base::base; // inherit constructor
};
int main() {
Tree tree = Node {
{ "a", Node { { "one", 1 }, { "two", 2 }, { "three",3 } } },
{ "b", Node {
{ "b1", Node { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
{ "b2", Node { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
}
},
{ "c", Node {} },
};
}
如果使用unordered_map
,当映射类型仍然不完整时可以实例化¹,则根本不需要recursive_wrapper
的性能影响。
在这个过程中,我们可以使构造函数更智能,树结构更简洁:
<强> Live On Coliru 强>
#include <boost/variant.hpp>
#include <boost/unordered_map.hpp>
struct Data {
int _i;
Data(int i = 0) : _i(i) {}
}; // just for illustration
struct Node : boost::variant<Data, boost::unordered_map<std::string, Node> > {
using Map = boost::unordered_map<std::string, Node>;
using Base = boost::variant<Data, Map>;
using Base::variant;
using Base::operator=;
Node(std::initializer_list<Map::value_type> init) : Base(Map(init)) {}
};
int main() {
auto tree = Node {
{ "a", { { "one", 1 }, { "two", 2 }, { "three", 3 } } },
{ "b", {
{ "b1", { { "four", 4 }, { "five", 5 }, { "six", 6 } } },
{ "b2", { { "seven", 7 }, { "eight", 8 }, { "nine", 9 } } },
}
},
{ "c", {} },
};
}
¹(我认为c ++ 17将其添加到标准库规范中)
答案 1 :(得分:0)
您将需要一个指针,该变量可以是叶子数据,也可以是指向另一个叶子的指针。
答案 2 :(得分:0)
我能够使用boost :: make_recursive_variant方法,但它需要为变体访问者提供额外的处理程序。
using LeafData = int; // just for illustration
using LeafNode = std::unordered_map<std::string, LeafData>;
using InnerNode = std::unordered_map<std::string, boost::make_recursive_variant<boost::recursive_variant_, LeafNode>::type>;
变体访问者需要处理三种类型,LeafNode,InnerNode,AND,InnerNode :: mapped_type(这是递归变体),然后编译器似乎很高兴。不确定这是否会&#34;工作&#34;但是,所以需要跑步,看看行为如何。