Я застрял с проблемой дублирования кода, касающейся шаблона посетителя для дерева. Текущая ситуация такова: у меня есть дерево, состоящее из двух разных классов узлов, то есть листьев и не листов. Кроме того, у меня есть два базовых класса для посетителей, которые очень похожи, за исключением того, что один посещает константные деревья, а другой — неконстантные. Фактические действия, которые должны выполнять конкретные посетители, не зависят от конкретных типов узла. Я приведу короткий пример:
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
повсюду?
Вы могли бы определить 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;
Вы можете использовать шаблоны:
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> {
};