简体   繁体   English

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

[英]Template for traversing a tree node using boost::variant

Here is my design for traversal of a node tree: 这是我遍历节点树的设计:

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

So I recursively visit the nodes until I arrive at a leaf. 所以我递归地访问节点,直到我到达一片叶子。 The problem above is that I must add a stub for operater() for each leaf. 上面的问题是我必须为每个叶子添加一个操作符()的存根。 You can see that I have five such stubs above but have many more in practice. 您可以看到我上面有五个这样的存根,但在实践中还有更多。 Can you suggest a way of templating this stub? 你能建议一种模仿这个存根的方法吗?

SOLUTION 1: SFINAE-based technique 解决方案1:基于SFINAE的技术

This solution is based on the fact that failing to substitute template parameters during the instantiation of a template does not cause a compilation error ( Substitution Failure Is Not An Error ): instead, that template is simply disregarded for overload resolution. 此解决方案基于以下事实:在模板实例化期间未能替换模板参数不会导致编译错误( 替换失败不是错误 ):相反,该模板只是被忽略以用于重载解析。 Thus, with some trick you can select which overloads of a certain function template shall be made visible depending on the template arguments provided at instantiation time. 因此,通过一些技巧,您可以根据实例化时提供的模板参数选择某个函数模板的哪些重载可见。

When this technique is used, it is important to make sure that the discriminating conditions which decide the visibility of each overload are mutually exclusive , or ambiguity may arise. 当使用这种技术时,重要的是确保决定每个过载的可见性的区别条件是相互排斥的 ,或者可能出现模糊。

To begin with, you need to define some trait metafunction that helps you determine whether a certain class is a leaf: 首先,您需要定义一些特征元函数,以帮助您确定某个类是否为叶子:

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

Then, you can use std::enable_if (or boost::enable_if if you are working with C++98) to select which overload of the call operator should be made visible: 然后,您可以使用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
    {
        ...
    }
};


SOLUTION 2: overload-based technique 解决方案2:基于过载的技术

If you are working on C++98 and do not want to use boost::enable_if as a replacement for std::enable_if , an alternative approach consists in exploiting overload resolution and an unused argument for discriminating between two overloads of a helper function. 如果您正在使用C ++ 98并且不想使用boost::enable_if作为std::enable_if的替代,则另一种方法是利用重载解析和未使用的参数来区分辅助函数的两个重载。 First of all, you define two dummy classes: 首先,您定义了两个虚拟类:

 struct true_type { }; struct false_type { }; 

Then, you create your is_leaf<> metafunction again, properly specializing it for leaf classes: 然后,再次创建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; }; ... 

Finally, you create an instance of one of those dummy types to choose the proper overload of a helper function process() : 最后,创建其中一个虚拟类型的实例,以选择辅助函数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 { ... } }; 

Use an additional level of indirection for the LeafN class, something like: LeafN类使用额外的间接级别,例如:

template <typename LeafType>
struct LeafHolder
{
  // has real instance of leaf..
};

Then redefine your variant type 然后重新定义您的变体类型

typedef boost::variant< LeafHolder<Leaf4>, LeafHolder<Leaf5> > Node3;
typedef boost::variant< LeafHolder<Leaf2>, LeafHolder<Leaf3>, Node3> Node2;
typedef boost::variant< Node2, LeafHolder<Leaf1 > Node1;

Now your visitor can become: 现在您的访问者可以成为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM