3

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

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() 添加一个存根。你可以看到我上面有五个这样的存根,但在实践中还有更多。你能建议一种模板化这个存根的方法吗?

4

2 回答 2

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
    {
        ...
    }
};
于 2013-01-24T14:38:20.157 回答
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{}
};
于 2013-01-24T14:36:31.160 回答