Посетитель и шаблонные виртуальные методы - PullRequest
10 голосов
/ 22 мая 2010

В типичной реализации шаблона Visitor класс должен учитывать все варианты (потомки) базового класса. Во многих случаях один и тот же контент методов в посетителе применяется к разным методам. В этом случае идеальным будет виртуальный шаблонный шаблон, но пока это запрещено.

Итак, можно ли использовать шаблонные методы для разрешения виртуальных методов родительского класса?

Дано (основание):

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 . Это вопрос против общего языка.)

Ответы [ 2 ]

6 голосов
/ 22 мая 2010

о, я вижу, что вы после.Попробуйте что-то вроде этого:



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 () на самом деле является метафункцией, которая строится на основе любого типа, к которому она обращается.Довольно круто и странно на самом деле.

2 голосов
/ 22 мая 2010

В вашем производном классе посетителей, Visitor_Cout, шаблон operator() не переопределяет operator() в Visitor_Base. Согласно стандарту C ++ 03 (14.5.2 / 4):

Специализация шаблона функции-члена не переопределяет виртуальную функцию из базового класса. [Пример:

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

- конец примера]

...