Использование enums и switch вместо шаблона посетителя

У меня есть дерево, которое представляет математическое выражение, и я подумал, что для вычисления значения дерева выражений я должен реализовать шаблон посетителя, однако в C ++ это требует многократного повторения, в качестве методов для принятия посетитель должен быть в каждом подклассе, потому что, хотя методы идентичны, типы не являются.

class Node {
virtual void Acccept(Visitor *visitor) = 0;
};
class ConstantNode : Node {
virtual void Accept(Visitor *visitor) {
visitor->visit( this );
}
};

class VariableNode : Node {
virtual void Accept( Visitor *visitor) {
visitor->visit( this );
}
};

class Visitor {
virtual void visit(ConstantNode *node) = 0;
virtual void visit(VariableNode *node) = 0;
};
class CalculateVisitor : Visitor {
virtual void visit(ConstantNode *node) { /* code */ }
virtual void visit(VariableNode *node) { /* code */ }
};

Это также имеет проблему в том, что, поскольку методы являются виртуальными, вы не можете иметь узлы шаблонов.

Просто кажется, что enum намного проще, так как для каждого узла есть один случай, и вы просто включаете enum в методе, а не в шаблоне посетителя.

class Node {
enum NodeType {
Constant,
Variable
}
Node(NodeType type) : m_nodeType(type) {}
NodeType m_nodeType;
};

class ConstantNode {
ConstantNode() : Node(Constant) {}
};
class VariableNode {
VariableNode() : Node(Variable) {}
};

int calculate(Node* node) {
switch (node->m_nodeType) {
case Constant:
//...
case Variable:
//...
}
}

Я знаю, что версия enum потребует динамического приведения, но в целом, кажется, предпочтительнее повторять большую часть одного и того же кода, и это означает, что это позволит использовать узлы шаблонов, например (BinOpNode<std::plus>).

В качестве альтернативы, есть ли способ улучшить шаблон посетителя в C ++, чтобы он не нуждался во всем этом повторении?

(Введите код непосредственно в stackoverflow, извините за любые ошибки, но он основан на реальном коде)

РЕДАКТИРОВАТЬ: для BinOpNode:

template<class T>
class BinOpNode {
T m_op = T();
BinOpNode() : Node(BinOp) {}
};

//in calculate:
case BinOpNode:
BinOpNode* n = dynamic_cast<BinOpNode*>(node);
return n->m_op(calculate(n->m_left), calculate(n->m_right));

3

Решение

Вы можете удалить повтор, используя шаблоны:

// Classes implementing the mechanism

class Node {
virtual void Accept(Visitor *visitor) = 0;
};

template<typename NodeType> class ConcreteNode
{
virtual void Accept(Visitor* visitor)
{
visits<NodeType>* v = visitor;
v->visit((NodeType*)this);
}
};

template<typename NodeType> class visits
{
virtual void visit(NodeType* node) = 0;
};

// The actual visitors/visited classes

class Visitor:
visits<ConstantNode>,
visits<VariableNode>
{
};

class ConstantNode : ConcreteNode<ConstantNote> {};
class VariableNode : ConcreteNode<VariableNode> {};

class CalculateVisitor : Visitor {
virtual void visit(ConstantNode *node) { /* code */ }
virtual void visit(VariableNode *node) { /* code */ }
};

Обратите внимание, что код вашего шаблона будет не работать, так как следующая строка не может работать:

BinOpNode* n = dynamic_cast<BinOpNode*>(node);

BinOpNode вы определяете как шаблон, и вы не можете иметь указатель на шаблон, только указатель на класс. Вы можете, например, иметь указатель на конкретный класс, сгенерированный из BinOpNode, лайк

BinOpNode<int>* n = dynamic_cast<BinOpNode<int>*>(node);

но это прекрасно обрабатывается и моделью посетителя; с кодом шаблона выше:

class Visitor:
public visits<BinOpNode<int> >,
public visits<BinOpNode<double> >,
...
{
};

template<typename T> class BinOpNode:
public ConcreteNode<BinOpNode<T> >
{
...
};
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]