关于c ++:访问者和模板化虚拟方法

Visitor and templated virtual methods

在访问者模式的典型实现中,类必须考虑到基类的所有变体(后代)。在许多情况下,访问者中相同的方法内容应用于不同的方法。在这种情况下,模板化虚拟方法是理想的,但目前不允许这样做。

那么,模板化方法可以用来解析父类的虚拟方法吗?

给予(基金会):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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);
  }
};

既然地基已经铺设好了,这里就是踢球者进来的地方(模板方法):

1
2
3
4
5
6
7
8
9
struct Visitor_Cout : public Visitor_Base
{
  template <class Receiver>
  void operator() (Receiver& r)
  {
     std::cout <<"Visitor_Cout method not implemented.
"
;
  }
};

故意地,Visitor_Cout在方法声明中不包含关键字virtual。方法签名的所有其他属性都与父声明(或者可能是规范)匹配。

在大图中,这种设计允许开发人员实现常见的访问功能,这些功能仅因目标对象(接收访问的对象)的类型而异。上面的实现是在派生的访问者实现没有实现可选方法时发出警报的建议。

这是合法的C++规范吗?

(我不相信有人说它与编译器XXX一起工作。这是一个反对通用语言的问题。)


哦,我知道你在找什么。尝试如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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!"; }
};

也就是说,您应该研究非循环访问者模式。它误解了框架中内置的访问者,因此您不必为永远不会调用的内容实现函数。

有趣的是,我实际上使用了与此非常相似的东西来为一个类型列表构建一个非循环的访问者。我应用了一个元函数,它基本上构建了一个时髦的访问者库,并将一个操作符(类似于我展示的apply()的东西)转换为完整列表的访问者。这些对象是反射的,因此apply()本身实际上是一个元函数,它基于其命中的任何类型构建。其实很酷很奇怪。


在派生的访问者类Visitor_Cout中,operator()模板不会覆盖Visitor_Base中的operator()。按照C++ 03标准(145.2/4):

A specialization of a member function template does not override a virtual function from a base class. [Example:

1
2
3
4
5
6
7
8
9
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
};

—end example]