使用 boost::variant 遍历树节点的模板
Template for traversing a tree node using boost::variant
这是我对节点树遍历的设计:
struct Leaf1{};
struct Leaf2{};
struct Leaf3{};
struct Leaf4{};
struct Leaf5{};
typedef boost::variant< Leaf4, Leaf5 > Node3;
typedef boost::variant< Leaf2, Leaf3, Node3> Node2;
typedef boost::variant< Node2, Leaf1 > Node1;
class NodeVisitor: public boost::static_visitor<void>
{
public:
template<class Node>
void operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}
void operator()(const Leaf1& e) const{}
void operator()(const Leaf2& e) const{}
void operator()(const Leaf3& e) const{}
void operator()(const Leaf4& e) const{}
void operator()(const Leaf5& e) const{}
};
所以我递归地访问节点,直到我到达一片叶子。上面的问题是我必须为每个叶子添加一个 operator() 的存根。你可以看到我上面有五个这样的存根,但在实践中还有更多。你能建议一种模板化这个存根的方法吗?
解决方案 1:基于 SFINAE 的技术
此解决方案基于这样一个事实,即在模板实例化期间未能替换模板参数不会导致编译错误(替换失败不是错误):相反,对于重载解析,只需忽略该模板。因此,通过一些技巧,您可以根据实例化时提供的模板参数选择某个函数模板的哪些重载是可见的。
使用这种技术时,重要的是要确保决定每个过载可见性的判别条件是相互排斥的,否则可能会出现歧义。
首先,您需要定义一些特征元函数,以帮助您确定某个类是否为叶:
// Primary template
template<typename T> struct is_leaf<T> { static const bool value = false; };
// Specializations...
template<> struct is_leaf<Leaf1> { static const bool value = true; };
template<> struct is_leaf<Leaf2> { static const bool value = true; };
...
然后,您可以使用std::enable_if
(如果您正在使用 C++98,则boost::enable_if
)来选择应使呼叫运营商的哪个重载可见:
class NodeVisitor: public boost::static_visitor<void>
{
public:
// Based on the fact that boost::variant<> defines a type list called
// "types", but any other way of detecting whether we are dealing with
// a variant is OK
template<typename Node>
typename std::enable_if<
!is_same<typename Node::types, void>::value
>::type
operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}
// Based on the fact that leaf classes define a static constant value
// called "isLeaf", but any other way of detecting whether we are dealing
// with a leaf is OK
template<typename Leaf>
typename std::enable_if<is_leaf<Leaf>::value>::type
operator()(const Leaf& e) const
{
...
}
};
解决方案2:基于过载的技术
如果您正在使用 C++98 并且不想使用 boost::enable_if
作为 std::enable_if
的替代品,另一种方法是利用重载分辨率和未使用的参数来区分帮助程序函数的两个重载。首先,你定义两个虚拟类:
struct true_type { };
struct false_type { };
然后,再次创建is_leaf<>
元函数,将其适当地专用于叶类:
// Primary template
template<typename T> struct is_leaf<T> { typedef false_type type; };
// Specializations...
template<> struct is_leaf<Leaf1> { typedef true_type type; };
template<> struct is_leaf<Leaf2> { typedef true_type type; };
...
最后,创建其中一个虚拟类型的实例,以选择帮助程序函数的适当重载process()
:
class NodeVisitor: public boost::static_visitor<void>
{
public:
template<typename T>
void operator()(const T& e) const
{
typedef typename is_leaf<T>::type helper;
process(e, helper());
}
template<typename Node>
void process(const Node& e, false_type) const
{
boost::apply_visitor(*this, e);
}
template<typename Leaf>
void process(const Leaf& e, true_type) const
{
...
}
};
对LeafN
类使用额外的间接寻址级别,如下所示:
template <typename LeafType>
struct LeafHolder
{
// has real instance of leaf..
};
然后重新定义变体类型
typedef boost::variant< LeafHolder<Leaf4>, LeafHolder<Leaf5> > Node3;
typedef boost::variant< LeafHolder<Leaf2>, LeafHolder<Leaf3>, Node3> Node2;
typedef boost::variant< Node2, LeafHolder<Leaf1 > Node1;
现在,您的访客可以成为:
class NodeVisitor: public boost::static_visitor<void>
{
public:
template<class Node>
void operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}
// single function to handle all leaves...
template <typename LeafType>
void operator()(const LeafHolder<LeafType>& e) const{}
};
相关文章:
- C++RapidXml-使用first_node()遍历以修改XML文件中节点的值
- 将树节点添加到向量向量中的 n 元树遍历的平均和最坏情况时间复杂度是多少?
- 二叉树级别顺序遍历在leetcode中
- 在单链表前面添加一个节点后,我无法遍历每个节点
- C++ 表达式树遍历以就地将表达式更改为 NNF
- Node中的子节点列表在C++中的BFS遍历过程中丢失
- 如何遍历具有两个节点的链接节点
- 遍历仅引用每个节点的子节点的树
- 二叉树顺序遍历在C++
- 二叉树顺序遍历错误:没有用于调用的匹配函数
- 使用级别顺序遍历将节点插入二叉树
- 递归后序遍历解除分配二叉树节点
- 跟踪节点遍历调用 std::map::find
- 无法遍历树 - 节点被重新遍历了大量次,但仍然是非循环的
- 二叉树顺序遍历导致堆栈溢出
- 双链接双向 n 树的遍历
- 序遍历的二叉搜索树,没有节点指针作为参数
- Boost 1.46.1,属性树:如何遍历ptree接收子ptree
- C++ 了解另一个程序员B树的遍历
- 使用 boost::variant 遍历树节点的模板