我正在用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;
};
然后,例如,我可以为某些MessageSubscriber
,MessageType
实现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::subscribers
时MessagePublisher::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>
我是否错误地按预期对功能模板进行了专业化处理,或者还有其他更有效的解决方案?对于与重载和成员模板专业化的概念有关的任何不准确的措辞,我都表示歉意。
答案 0 :(得分:0)
您可以使用C样式的函数指针代替虚函数,以消除一级间接访问。因此,在基类的声明中,您可能会有类似的内容:
void (*) OnMessage (BaseClass *self, MessageType *message);
然后,您在每个派生类的构造函数中初始化此实例变量,以指向适当的静态成员函数,从而使您可以通过单个间接调用(而不是通过vtable进行两次调用)进行调用)。
最后,可悲的是,您将需要在派生类的每个目标函数中强制转换self
,这是您为所有这些技巧所付出的代价。分配函数指针时,要么强制转换,要么强制转换函数签名。如果有兴趣,我会发布一个更完整的示例-让我知道。