4

質問は理論的なものです。

序文。 訪問者パターン:

class Visitor
{
public:
    virtual void VisitElementA(const ElementA& obj) = 0;
    virtual void VisitElementB(const ElementB& obj) = 0;
};

class Element
{
public:
    virtual void Accept(Visitor& visitor) = 0;
};

class ElementA : public Element
{
public:
    void Accept(Visitor& visitor) override { visitor.VisitElementA(*this); }
};

class ElementB : public Element
{
public:
    void Accept(Visitor& visitor) override { visitor.VisitElementB(*this); }
};

この VisitElementA(const ElementA& obj) は少し見栄えが悪いので、オーバーロードを使用して次のように書き換えることができます。

class Visitor
{
public:
    virtual void Visit(const ElementA& obj) = 0;
    virtual void Visit(const ElementB& obj) = 0;
};

これで、ElementA と ElementB に Accept メソッドの 2 つの同一の実現ができました。

void Accept(Visitor& visitor) override { visitor.Visit(*this); }

そして、そのようなコードを ElementC、ElementD などに追加する必要があります (存在する場合)。

問題は次のとおりです。この重複を回避するにはどうすればよいですか?

Accept 実現を Element クラス (またはその他の中間クラス) 内に配置する単純な解決策は機能しません。これは、このポインターが、ElementA または ElementB ではなく、クラス Element のオブジェクトとしてオブジェクトを指すためです。コンパイル エラーが発生したり、間違った動作が発生したりします (Element にオーバーロードされた Visit メソッドが存在する場合)。

私が理解している限り、問題はコンパイル時機能と実行時機能を混在させようとすることにあります。しかし、テンプレートベースのソリューションや新しい C++11 機能、またはその他の何かが存在する可能性はありますか?

1つの注意:「マクロマジック」で解決策を提供しないでいただければ幸いです:)。

4

2 に答える 2

2

CRTPパターンを使用できます。

Elementクラスを、派生型を型パラメーターとして受け取るテンプレート クラスに変換します。次に、ビジターを呼び出す前に派生型にダウンキャストできます。

template <typename Derived>
class Element
{
public:
    void Accept(Visitor& visitor) { visitor.Visit(*static_cast<Derived*>(this)); }
};

最後に、各具象要素は次のように派生しElementます。

class ElementA : public Element<ElementA>
{
};

Accept(Visitor&)仮想である必要がなくなったことにも注意してください。

更新: ケツァルコアトルが指摘した問題の解決策は次のとおりです。

class ElementC : public Element<ElementC>, public ElementA
{
public:
    using Element<ElementC>::Accept;
};

using 宣言ElementCにより、Accept名前がそのスコープに取り込まれ、その結果、基本クラスの名前が非表示になります。ただし、これAcceptElement<ElementC>::Accept実際にElementA::Acceptは隠されているだけです。

于 2013-03-23T08:45:52.360 に答える
0
class Visitor
{
public:
    virtual void Visit(const Element& obj) = 0;
}
class Element
{
public:
    void Accept(Visitor& visitor) { visitor.Visit(*this); }
};

Edit2: Element 基本クラスから Visit メソッドを呼び出すだけです。Elements はポリモーフであるため、正しいオブジェクトを渡しました。ただし、要素に固有のメソッドにアクセスする必要がある場合や、他の抽象メソッドを導入する必要がある場合は、キャストが必要になる場合があります。

于 2013-03-23T08:10:23.543 に答える