使用 boost::variant 遍历树节点的模板

Template for traversing a tree node using boost::variant

本文关键字:树节点 遍历 boost variant 使用      更新时间:2023-10-16

这是我对节点树遍历的设计:

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{}
};