Шаблон посетителя для константных и неконстантных версий дерева

Я застрял с проблемой дублирования кода в отношении шаблона посетителя для дерева. Текущая ситуация такова: у меня есть дерево, состоящее из двух разных классов узлов, то есть листьев и не листьев. Вдобавок у меня есть два базовых класса посетителей, которые очень похожи, за исключением того, что один посещает константные деревья, а другой - неконстантные деревья. Фактические действия, которые должны выполнить конкретные посетители, не зависят от конкретных типов узла. Приведу небольшой пример:

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

Конкретные классы посетителей наследуются либо от Visitor, либо от ConstVisitor, в зависимости от того, должен ли их processNode метод изменять посещенные узлы или нет.

Видите ли, существует много дублирования кода между двумя посетителями, и, поскольку мне придется реализовать другую стратегию обхода, также как для константных, так и для неконстантных узлов, я хочу избежать этого дублирования. Есть ли возможности извлечь повторяющийся код, желательно без использования const_cast повсюду?


person Arne Mertz    schedule 13.05.2013    source источник


Ответы (2)


Вы можете определить шаблон класса 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); }
    }
};

А затем используйте Visitor и ConstVisitor в качестве псевдонимов типов для соответствующих экземпляров этого шаблона класса:

typedef TVisitor<true> Visitor;
typedef TVisitor<false> ConstVisitor;
person Andy Prowl    schedule 13.05.2013
comment
Спасибо, это было быстрое и чистое решение. Надеюсь, мои коллеги не слишком ненавидят шаблоны ;-) - person Arne Mertz; 13.05.2013

Вы можете использовать шаблоны:

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> {
};
person Peter Wood    schedule 13.05.2013