【问题标题】:Visitor and templated virtual methods访问者和模板化虚拟方法
【发布时间】:2011-02-22 14:13:12
【问题描述】:

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。这是一个针对通用语言的问题。)

【问题讨论】:

  • 您的意思是Visitor_CoutVisitor_Base 作为基类吗?

标签: c++ templates operator-overloading visitor-pattern virtual-functions


【解决方案1】:

哦,我知道你在追求什么。试试这样的:



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() 本身实际上是一个元函数,它基于它所击中的任何类型构建。实际上很酷很奇怪。

【讨论】:

  • 这就是所谓的 CRTP(奇怪的重复模板模式)。谷歌一下。
  • 糟糕。 CRTP 不使用虚函数。我不清楚你的例子为什么会这样。
  • 我的例子有虚函数,因为它是一个访问者。 CRTP 与其说是一种模式,不如说是一种习惯用法,您会发现它在许多模式的实现中很有用。不知道为什么有人告诉我用谷歌搜索 CRTP:\...?
【解决方案2】:

在您的派生访问者类Visitor_Cout 中,operator() 模板不会覆盖Visitor_Base 中的operator()。根据 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
};

——结束示例]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 2016-09-28
    • 2011-12-19
    • 1970-01-01
    • 2022-12-04
    相关资源
    最近更新 更多