10

訪問者パターンの典型的な実装では、クラスは基本クラスのすべてのバリエーション (子孫) を考慮する必要があります。ビジター内の同じメソッド コンテンツが異なるメソッドに適用される場合が多数あります。この場合、テンプレート化された仮想メソッドが理想的ですが、現時点では許可されていません。

では、テンプレート化されたメソッドを使用して、親クラスの仮想メソッドを解決できますか?

与えられた(基礎):

struct Visitor_Base; // Forward declaration.

struct Base
{
  virtual accept_visitor(Visitor_Base& visitor) = 0;
};

// More forward declarations
struct Base_Int;
struct Base_Long;
struct Base_Short;
struct Base_UInt;
struct Base_ULong;
struct Base_UShort;

struct Visitor_Base
{
  virtual void operator()(Base_Int& b) = 0;
  virtual void operator()(Base_Long& b) = 0;
  virtual void operator()(Base_Short& b) = 0;
  virtual void operator()(Base_UInt& b) = 0;
  virtual void operator()(Base_ULong& b) = 0;
  virtual void operator()(Base_UShort& b) = 0;
};

struct Base_Int : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

struct Base_Long : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

struct Base_Short : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

struct Base_UInt : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

struct Base_ULong : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

struct Base_UShort : public Base
{
  void accept_visitor(Visitor_Base& visitor)
  {
     visitor(*this);
  }
};

基礎が築かれたので、キッカーの出番です (テンプレート化されたメソッド):

struct Visitor_Cout : public Visitor_Base
{
  template <class Receiver>
  void operator() (Receiver& r)
  {
     std::cout << "Visitor_Cout method not implemented.\n";
  }
};

意図的に、メソッド宣言Visitor_Coutにキーワードが含まれていません。virtualメソッド シグネチャの他のすべての属性は、親の宣言 (または仕様) と一致します。

全体像として、この設計により、開発者は、ターゲット オブジェクト (訪問を受け取るオブジェクト) のタイプのみが異なる共通の訪問機能を実装できます。上記の実装は、派生ビジターの実装がオプションのメソッドを実装していない場合のアラートに対する私の提案です。

これは C++ 仕様で合法ですか?

(コンパイラXXXで動作すると言う人もいますが、これは一般的な言語に対する質問です。)

4

2 に答える 2

6

ああ、あなたが何を求めているか分かります。次のようなことを試してください:



template < typename Impl >
struct Funky_Visitor_Base : Visitor_Base
{
  // err...
  virtual void operator()(Base_Int& b) { Impl::apply(b) }
  virtual void operator()(Base_Long& b) { Impl::apply(b) }
  virtual void operator()(Base_Short& b) { Impl::apply(b) }
  virtual void operator()(Base_UInt& b) { Impl::apply(b) }
  virtual void operator()(Base_ULong& b) { Impl::apply(b) }

  // this actually needs to be like so:
  virtual void operator()(Base_UShort& b)
  {
    static_cast<impl *const>(this)->apply(b) 
  }
};

struct weird_visitor : Funky_Visitor_Base<weird_visitor>
{
  // Omit this if you want the compiler to throw a fit instead of runtime error.
  template < typename T >
  void apply(T & t)
  {
    std::cout << "not implemented.";
  }

  void apply(Base_UInt & b) { std::cout << "Look what I can do!"; }
};

そうは言っても、非循環的な訪問者パターンを調べる必要があります。フレームワークに組み込まれている訪問者を誤解しているため、決して呼び出さないもののために関数を実装する必要はありません。

面白いことに、これと非常によく似たものを実際に使用して、型のリストの非循環ビジターを作成しました。基本的に Funky_Visitor_Base を構築し、オペレーター (私が示したような apply() を含むもの) をその完全なリストのビジターに変換するメタ関数を適用しました。オブジェクトはリフレクティブであるため、apply() 自体は実際には、ヒットする型に基づいて構築されるメタ関数です。実際にはかなりクールで奇妙です。

于 2010-05-21T23:28:37.270 に答える
2

派生ビジター クラス ではVisitor_Cout、テンプレートは の をオーバーライドしoperator()ません。C++03 標準 (14.5.2/4) によると:operator()Visitor_Base

メンバー関数テンプレートの特殊化は、基本クラスの仮想関数をオーバーライドしません。[例:

class B {
    virtual void f(int);
};

class D : public B {
    template <class T> void f(T);  // does not override B::f(int)
    void f(int i) { f<>(i); }      // overriding function that calls
                                   // the template instantiation
};

—終わりの例]

于 2010-05-21T23:24:34.960 に答える