2016-08-03 5 views
2

私は数式を表すツリーを持っています。私は式ツリーの値を計算するために訪問者パターンを実装すると考えましたが、C++ではメソッドが同一であっても、型はそうではないので、訪問者を受け入れるメソッドがすべてのサブクラスになければならないので、自分自身を繰り返したくさんあります。訪問者パターンの代わりにenumsとswitchを使用する

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 */ } 
}; 

これは、メソッドが仮想であるため、テンプレートノードを持つことができないという問題もあります。

各ノードごとに1つのケースがあり、ビジターパターンではなくメソッドで列挙型をオンにするだけで、列挙型の方がはるかに簡単です。

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: 
     //... 
    } 
} 

私は、列挙型のバージョンは動的なキャストが必要になります知っているが、全体的に同じコードの多くを繰り返す有することが好ましいようで、それがテンプレートノードなど(BinOpNode<std::plus>)を可能にすることを意味します。

また、この繰り返しをすべて行う必要がないように、C++でビジターパターンを改善する方法がいくつかありますか?

(間違いのため申し訳ありませんが、stackoverflowのに直接コードを型付きが、それは実際のコードに基づいています)

EDIT: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)); 
+1

これはまったく簡単ではないようです。反復の量は同じです(ケースラベルと仮想関数)。また、テンプレートの処理方法についても説明していませんでした。 'case BinOp:'今何? –

+0

すべての受諾機能が不要になるため、繰り返し回数が少なくなります。いずれの状況でもノードのコンストラクタが存在します。 –

+0

新しい訪問者を追加するのに繰り返しは必要ありません。 switch/enumの場合とは逆の型を忘れることは避けてください。 – Jarod42

答えて

1

あなたは、テンプレートを使って、繰り返しを削除することができます。

// 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<int>* n = dynamic_cast<BinOpNode<int>*>(node); 

のように、BinOpNodeから生成特定クラスへのポインタを持っているかもしれないが、それは完全にもビジターパターンによって処理されます。上のテンプレートコード:

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

template<typename T> class BinOpNode: 
    public ConcreteNode<BinOpNode<T> > 
{ 
    ... 
}; 
関連する問題