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

时间:2013-01-24 14:24:11

标签: c++ c++98

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

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

所以我递归地访问节点,直到我到达一片叶子。上面的问题是我必须为每个叶子添加一个操作符()的存根。您可以看到我上面有五个这样的存根,但在实践中还有更多。你能建议一种模仿这个存根的方法吗?

2 个答案:

答案 0 :(得分:3)

解决方案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(或boost::enable_if,如果您正在使用C ++ 98)来选择应该使调用运算符的哪个重载可见:

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

答案 1 :(得分:2)

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