我正在尝试编写以下情况的代码:
我有一个基类,提供了处理事件的框架。我正在尝试为此使用指针到成员函数的数组。它如下:

class EH { // EventHandler
   virtual void something(); // just to make sure we get RTTI
public:
  typedef void (EH::*func_t)();
protected:
  func_t funcs_d[10];
protected:
  void register_handler(int event_num, func_t f) {
    funcs_d[event_num] = f;
  }
public:
  void handle_event(int event_num) {
    (this->*(funcs_d[event_num]))();
  }
};

然后,应该假定用户从这一类派生其他类并提供处理程序:
class DEH : public EH {
public:
  typedef void (DEH::*func_t)();
  void handle_event_5();
  DEH() {
     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile
     ........
  }
};

由于无法将DEH::func_t转换为EH::func_t,因此无法编译该代码。这对我来说很有意义。在我的情况下,转换是安全的,因为this下的对象实际上是DEH。所以我想要这样的东西:
void EH::DEH_handle_event_5_wrapper() {
  DEH *p = dynamic_cast<DEH *>(this);
  assert(p != NULL);
  p->handle_event_5();
}

然后代替
     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile

在DEH::DEH()中

     register_handler(5, &EH::DEH_handle_event_5_wrapper);

所以,最后一个问题(花了我足够长的时间……):
有没有一种方法可以自动创建这些包装器(例如EH::DEH_handle_event_5_wrapper)?
还是要做类似的事情?
对于这种情况还有什么其他解决方案?

谢谢。

最佳答案

不必为所有派生类中的每个处理程序创建包装器(当然,甚至不是远程可行的方法),您只需使用static_castDEH::func_t转换为EH::func_t即可。成员指针是互变的:它们自然地向下转换到层次结构,并且可以使用static_cast(与普通对象指针相反,它们是协变的)手动向上转换到层次结构。

您正在处理的情况正是static_cast功能得以扩展以允许成员指针向上转换的原因。而且,成员函数指针的非平凡的内部结构也以这种方式实现,以正确地处理这种情况。

因此,您只需

DEH() {
   func_t f5 = &DEH::handle_event_5;
   register_handler(5, static_cast<EH::func_t>(f5));
   ........
}

我要说的是,在这种情况下,没有必要定义typedef名称DEH::func_t-这毫无用处。如果删除DEH::func_t的定义,则典型的注册代码如下所示
DEH() {
   func_t f5 = static_cast<func_t>(&DEH::handle_event_5);
   // ... where `func_t` is the inherited `EH::func_t`
   register_handler(5, f5);
   ........
}

为了使外观更加美观,您可以在register_handler中提供DEH的包装,也可以使用其他方式(宏?模板)来隐藏 Actor 表。

该方法没有为您提供任何方法在调用时验证处理程序指针的有效性(就像您可以在基于包装的版本中使用dynamic_cast一样)。我不知道您到底有多想在这里进行检查。我想说,在这种情况下,这实际上是不必要和过度的。

10-06 13:35