c – 使用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{}
};
所以我递归地访问节点,直到我到达一片叶子.上面的问题是我必须为每个叶子添加一个操作符()的存根.您可以看到我上面有五个这样的存根,但在实践中还有更多.你能建议一种模仿这个存根的方法吗?
解决方法:
解决方案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
{
...
}
};