C ++静态多态性–引用专业模板方法,这些方法从基类指针的派生类中重载

时间:2018-12-25 20:31:39

标签: c++ templates virtual-functions static-polymorphism static-dispatch

我正在用C ++实现观察者模式的变体。但是,由于我项目的性质,它不能使用任何虚拟成员功能,因为来自vtable查找和缓存未命中的总开销是不可接受的。

如果我要通过虚拟成员函数创建接口,我会简单地编写以下内容:

template <class MessageType>
class MessageSubscriber {
public:
  virtual void OnMessage(MessageType *message) = 0;
};

template <class MessageType>
class MessagePublisher {
public:
  void AddSubscriber(MessageSubscriber<MessageType> *subscriber) {
    subscribers.push_back(subscriber);
  } 

protected:
  void Publish(MessageType *message) {
    for (auto subscriber : subscribers)
      subscriber.OnMessage(message);
  }

private:
  std::vector<MessageSubscriber<MessageType>*> subscribers;
};

然后,例如,我可以为某些MessageSubscriberMessageType实现SafetyMessage的类,如下所示:

class SafetyMessageSubscriberA : public MessageSubscriber<SafetyMessage> {
public:
  virtual void OnMessage(SafetyMessage *message) override {
    /* process message */
  }
};

class SafetyMessageSubscriberB : public MessageSubscriber<SafetyMessage> {
public:
  virtual void OnMessage(SafetyMessage *message) override {
    /* process message */
  }
};

class SafetyMessagePublisher : public MessagePublisher<SafetyMessage> {
public:
  void Run {
    /* manipulate message data */
    this->Publish(&message);
  }
private:
  SafetyMessage message;
};

这可以完成工作,但是,如前所述,vtable查找开销在应用程序上下文中是不可接受的,尽管它提供了多态性,并且也是应用程序所需的。然后,自然地,我尝试了几种围绕静态多态性的方法,这些静态多态性可以通过模板来利用。

我首先尝试利用CTRP,但是在这种情况下失败了,因为调用MessagePublisher::subscribersMessagePublisher::Publish(MessageType *message)中包含的指针必须指向相同的基类。如此,您在MessageSubscriber<SafetyMessageSubscriberA>MessageSubscriber<SafetyMessageSubscriberB>的行上就不可能有一些CTRP模式,因为模板参数必须是相同的,两个对象才能在MessagePublisher::subscribers中合法地被允许。 / p>

我最近对该问题的尝试使我尝试了一些成员函数模板专业化的变体,尽管没有成功。我已经在模式界面上尝试了以下变体:

class MessageSubscriber {
public:
  template <class MessageType>
  void OnMessage(MessageType *message);
};

class MessagePublisher {
public:
  template <class MessageType>
  void Publish(MessageType *message) {
    for (auto subscriber: subscribers)
      subscriber->OnMessage<MessageType>(message);   
  }

private:
  std::vector<MessageSubscriber*> subscribers;
};

template<class MessageType> 
void MessageSubscriber::OnMessageOnMessage(MessageType *message) {
  /* "interface" call; do nothing */
}

具有以下实现:

class SafetyMessageSubscriberA : public MessageSubscriber {
public:
  // declare for legal overload
  template <class MessageType>
  void OnMessage(MessageType *message);
};

class SafetyMessageSubscriberB : public MessageSubscriber {
public:  
  // declare for legal overload
  template <class MessageType>
  void OnMessage(MessageType *message);  
};

template<>
void SafetyMessageSubscriberA::OnMessage<SafetyMessage*>OnMessage(SafetyMessage *message) {
  /* process message */
}

template<> 
void SafetyMessageSubscriberB::OnMessage<SafetyMessage*>OnMessage(SafetyMessage *message) {
  /* process message */
}

但是,当我尝试此操作时,MessagePublisher::Publish(SafetyMessage *message)将始终为基类调用通用的MessageSubscriber::OnMessage(MessageType *m)实现,而不是为特定于SafetyMessage*的派生类实现的实现。 / p>

我是否错误地按预期对功能模板进行了专业化处理,或者还有其他更有效的解决方案?对于与重载和成员模板专业化的概念有关的任何不准确的措辞,我都表示歉意。

1 个答案:

答案 0 :(得分:0)

您可以使用C样式的函数指针代替虚函数,以消除一级间接访问。因此,在基类的声明中,您可能会有类似的内容:

void (*) OnMessage (BaseClass *self, MessageType *message);

然后,您在每个派生类的构造函数中初始化此实例变量,以指向适当的静态成员函数,从而使您可以通过单个间接调用(而不是通过vtable进行两次调用)进行调用)。

最后,可悲的是,您将需要在派生类的每个目标函数中强制转换self,这是您为所有这些技巧所付出的代价。分配函数指针时,要么强制转换,要么强制转换函数签名。如果有兴趣,我会发布一个更完整的示例-让我知道。