C++ 树的常量和非常量版本的访问者模式

C++ 树的常量和非常量版本的访问者模式,c++,visitor,C++,Visitor,关于树的访问者模式,我遇到了代码重复问题。当前情况如下:我有一棵树,由两个不同的节点类组成,即leafs和non-leafs。此外,我有两个访问者基类,它们看起来非常相似,只是一个访问常量树,另一个访问非常量树。具体访问者必须执行的实际操作独立于节点的具体类型。我将举一个简短的例子: class Visitor; class ConstVisitor; class Node { public: virtual void accept(Visitor&) = 0; virtual

关于树的访问者模式,我遇到了代码重复问题。当前情况如下:我有一棵树,由两个不同的节点类组成,即leafs和non-leafs。此外,我有两个访问者基类,它们看起来非常相似,只是一个访问常量树,另一个访问非常量树。具体访问者必须执行的实际操作独立于节点的具体类型。我将举一个简短的例子:

class Visitor;
class ConstVisitor;

class Node {
public:
  virtual void accept(Visitor&) = 0;
  virtual void accept(ConstVisitor&) const = 0;
};

class Leaf : public Node {
  virtual void accept(Visitor& v)        {v.visitLeaf(*this);}
  virtual void accept(ConstVisitor& cv)  {cv.visitLeaf(*this);}
};

class CompoundNode : public Node {
public:
  vector<Node*> getChildren() const;
  virtual void accept(Visitor& v)        {v.visitCompoundNode(*this);}
  virtual void accept(ConstVisitor& cv)  {cv.visitCompoundNode(*this);}
};

class Visitor {
protected:
  virtual void processNode(Node& node) = 0;
public:
  void visitLeaf(Leaf& leaf) {
    processNode(leaf);
  }
  void visitCompoundNode(CompoundNode& cNode) {
    processNode(cNode);
    auto children = cNode.getChildren();
    for (auto child : children)
      child->accept(this);
  }
};

class ConstVisitor {
protected:
  virtual void processNode(Node const& node) = 0;
public:
  void visitLeaf(Leaf const& leaf) {
    processNode(leaf);
  }
  void visitCompoundNode(CompoundNode const& cNode) {
    processNode(cNode);
    auto children = cNode.getChildren();
    for (auto child : children)
      child->accept(this);
  }
};
类访问者;
班级访客;
类节点{
公众:
虚拟无效接受(访问者&)=0;
虚拟无效接受(ConstVisitor&)const=0;
};
类叶:公共节点{
虚拟无效接受(访问者&v){v.visitLeaf(*this);}
虚拟无效接受(ConstVisitor&cv){cv.visitLeaf(*this);}
};
类CompoundNode:公共节点{
公众:
向量getChildren()常量;
虚拟void接受(Visitor&v){v.visitCompoundNode(*this);}
虚拟void接受(ConstVisitor&cv){cv.visitCompoundNode(*this);}
};
班级访客{
受保护的:
虚拟节点(节点和节点)=0;
公众:
无效visitLeaf(叶和叶){
进程节点(叶);
}
无效visitCompoundNode(CompoundNode和cNode){
processNode(cNode);
auto children=cNode.getChildren();
用于(自动子对象:子对象)
孩子->接受(这个);
}
};
班主任访客{
受保护的:
虚拟void进程节点(节点常量和节点)=0;
公众:
无效visitLeaf(叶常数和叶){
进程节点(叶);
}
无效visitCompoundNode(CompoundNode常量和cNode){
processNode(cNode);
auto children=cNode.getChildren();
用于(自动子对象:子对象)
孩子->接受(这个);
}
};
具体的访问者类继承自
visitor
ConstVisitor
,这取决于它们的
processNode
方法是否必须更改所访问的节点


你看,这两个访问者之间有很多代码重复,因为我必须实现另一个遍历策略,同样对于常量和非常量节点,我希望避免这种重复。是否有可能提取重复的代码,最好不要到处使用
const\u cast

您可以定义
TVisitor
类模板,如下所示:

#include <type_traits>

class Node;
class CompoundNode;
class Leaf;

template<bool isNonConstVisitor>
class TVisitor
{
    typedef typename std::conditional<isNonConstVisitor, 
        Node, Node const>::type node_type;

    typedef typename std::conditional<isNonConstVisitor, 
        CompoundNode, CompoundNode const>::type compound_node_type;

    typedef typename std::conditional<isNonConstVisitor, 
        Leaf, Leaf const>::type leaf_node_type;

protected:

    virtual void processNode(node_type& node) = 0;

public:

    void visitLeaf(leaf_node_type& leaf) { processNode(leaf); }

    void visitCompoundNode(compound_node_type& cNode) {
        processNode(cNode);
        auto children = cNode.getChildren();
        for (auto child : children) { child->accept(*this); }
    }
};

您可以使用以下模板:

template<typename NodeType,
         typename CompoundNodeType,
         typename LeafType>
class BaseVisitor {
protected:
    virtual void processNode(NodeType& node) = 0;
public:
    void visitLeaf(LeafType& leaf) {
        processNode(leaf);
    }
    void visitCompoundNode(CompoundNodeType& cNode) {
        processNode(cNode);
        auto children = cNode.getChildren();
        for (auto child : children)
        child->accept(this);
    }
};

class Visitor: public BaseVisitor<Node, CompoundNode, Leaf> {
};

class ConstVisitor: public BaseVisitor<const Node, const CompoundNode, const Leaf> {
};
模板
类基本访问者{
受保护的:
虚拟void进程节点(节点类型和节点)=0;
公众:
无效visitLeaf(叶型和叶型){
进程节点(叶);
}
无效visitCompoundNode(CompoundNodeType和cNode){
processNode(cNode);
auto children=cNode.getChildren();
用于(自动子对象:子对象)
孩子->接受(这个);
}
};
类访问者:公共基础访问者{
};
类ConstVisitor:公共BaseVisitor{
};

谢谢,这是一个快速、干净的解决方案。我希望我的同事们不要太讨厌模板;-)
template<typename NodeType,
         typename CompoundNodeType,
         typename LeafType>
class BaseVisitor {
protected:
    virtual void processNode(NodeType& node) = 0;
public:
    void visitLeaf(LeafType& leaf) {
        processNode(leaf);
    }
    void visitCompoundNode(CompoundNodeType& cNode) {
        processNode(cNode);
        auto children = cNode.getChildren();
        for (auto child : children)
        child->accept(this);
    }
};

class Visitor: public BaseVisitor<Node, CompoundNode, Leaf> {
};

class ConstVisitor: public BaseVisitor<const Node, const CompoundNode, const Leaf> {
};