覆盖可变参数 class 模板中的多个虚函数
Overriding multiple virtual functions in a variadic class template
让我举一个代码示例。
#include <iostream>
#include <utility>
#include <tuple>
template <typename Service>
struct SubscriberImpl {
virtual void handleService(Service const&) = 0;
};
template <typename...ServiceType>
struct Subscriber : SubscriberImpl<ServiceType>... {
};
struct IntService {};
struct FloatService {};
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
// wont work
void handleService(IntService const& service) override {
m_statusUpdater.updateService(service);
}
void handleService(FloatService const& service) override {
m_statusUpdater.updateService(service);
}
StatusUpdatePolicy m_statusUpdater;
};
struct DummyPolicy {
void updateService(IntService const& service) {
m_i = 42;
std::cout << m_i << "\n";
}
void updateService(FloatService const& service) {
m_f = 3.14f;
std::cout << m_f << "\n";
}
int m_i;
float m_f;
};
int main() {
StatusUpdater<DummyPolicy, IntService, FloatService> su(DummyPolicy{});
su.handleService(IntService{});
su.handleService(FloatService{});
}
此处 Subscriber
包 ServiceType...
中的每个模板参数都有一个纯虚函数 handleService(ServiceType const)
。所以我必须在 StatusUpdater
上覆盖每一个。在这里,我已经为 IntService
和 FloatService
手动提供了我需要的那些,我知道在这个最小的例子中我只需要那些。但我希望能够为包 ServiceType...
中的任何内容提供覆盖。无论如何,它们都会调用给定策略的 updateService
方法。
请注意 Subscriber
来自外部库,我无法修改它的定义。
您不能将此类实现直接放入 class,您必须继承它们(类似于 Subscriber
从多个 SubscriberImpl
实例继承的方式)。但是,要覆盖它们并仍然保持您的 class 可多态地用作 Subscriber
,您将必须继承它们 "sequentially" 而不是 "in parallel." 此外,Curiously recurring template pattern 可用于为所有实现提供对最终覆盖对象的访问权限:
template <class Self, class SubscriberClass, class... ServiceTypes>
struct StatusUpdaterOverride;
template <class Self, class SubscriberClass, class ThisType, class... RemainingTypes>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType, RemainingTypes...> : StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
using StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>::handleService;
};
template <class Self, class SubscriberClass, class ThisType>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType> : SubscriberClass
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
};
template <class StatusUpdatePolicy, class... ServiceType>
struct StatusUpdater : StatusUpdaterOverride<StatusUpdater<StatusUpdatePolicy, ServiceType...>, Subscriber<ServiceType...>, ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
StatusUpdatePolicy m_statusUpdater;
};
我看不到完全符合您要求的解决方案。但是,您可以在根本不需要 virtual
ity 的情况下实现相同的行为。我最初想到了一个 CRTP 解决方案,就像@Angew 的回答一样,然后想出了另一种可能性:
您可以这样编辑 Subscriber
class:
template <typename ServiceType>
class Subscriber {
public:
template <typename Handler>
void handleService(ServiceType const& service, Handler&& hdler) {
// Maybe give `updateService` a broader name that can extend to other service handlers
std::forward<Handler>(hdler).updateService(service);
}
};
这样,您的客户端代码就变成了:
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType>...
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
template <typename ServiceT>
void handleService(ServiceT const& service) override {
Subscriber<ServiceT>::handleService(service, m_statusUpdater);
}
StatusUpdatePolicy m_statusUpdater;
};
让我举一个代码示例。
#include <iostream>
#include <utility>
#include <tuple>
template <typename Service>
struct SubscriberImpl {
virtual void handleService(Service const&) = 0;
};
template <typename...ServiceType>
struct Subscriber : SubscriberImpl<ServiceType>... {
};
struct IntService {};
struct FloatService {};
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
// wont work
void handleService(IntService const& service) override {
m_statusUpdater.updateService(service);
}
void handleService(FloatService const& service) override {
m_statusUpdater.updateService(service);
}
StatusUpdatePolicy m_statusUpdater;
};
struct DummyPolicy {
void updateService(IntService const& service) {
m_i = 42;
std::cout << m_i << "\n";
}
void updateService(FloatService const& service) {
m_f = 3.14f;
std::cout << m_f << "\n";
}
int m_i;
float m_f;
};
int main() {
StatusUpdater<DummyPolicy, IntService, FloatService> su(DummyPolicy{});
su.handleService(IntService{});
su.handleService(FloatService{});
}
此处 Subscriber
包 ServiceType...
中的每个模板参数都有一个纯虚函数 handleService(ServiceType const)
。所以我必须在 StatusUpdater
上覆盖每一个。在这里,我已经为 IntService
和 FloatService
手动提供了我需要的那些,我知道在这个最小的例子中我只需要那些。但我希望能够为包 ServiceType...
中的任何内容提供覆盖。无论如何,它们都会调用给定策略的 updateService
方法。
请注意 Subscriber
来自外部库,我无法修改它的定义。
您不能将此类实现直接放入 class,您必须继承它们(类似于 Subscriber
从多个 SubscriberImpl
实例继承的方式)。但是,要覆盖它们并仍然保持您的 class 可多态地用作 Subscriber
,您将必须继承它们 "sequentially" 而不是 "in parallel." 此外,Curiously recurring template pattern 可用于为所有实现提供对最终覆盖对象的访问权限:
template <class Self, class SubscriberClass, class... ServiceTypes>
struct StatusUpdaterOverride;
template <class Self, class SubscriberClass, class ThisType, class... RemainingTypes>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType, RemainingTypes...> : StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
using StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>::handleService;
};
template <class Self, class SubscriberClass, class ThisType>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType> : SubscriberClass
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
};
template <class StatusUpdatePolicy, class... ServiceType>
struct StatusUpdater : StatusUpdaterOverride<StatusUpdater<StatusUpdatePolicy, ServiceType...>, Subscriber<ServiceType...>, ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
StatusUpdatePolicy m_statusUpdater;
};
我看不到完全符合您要求的解决方案。但是,您可以在根本不需要 virtual
ity 的情况下实现相同的行为。我最初想到了一个 CRTP 解决方案,就像@Angew 的回答一样,然后想出了另一种可能性:
您可以这样编辑 Subscriber
class:
template <typename ServiceType>
class Subscriber {
public:
template <typename Handler>
void handleService(ServiceType const& service, Handler&& hdler) {
// Maybe give `updateService` a broader name that can extend to other service handlers
std::forward<Handler>(hdler).updateService(service);
}
};
这样,您的客户端代码就变成了:
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType>...
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
template <typename ServiceT>
void handleService(ServiceT const& service) override {
Subscriber<ServiceT>::handleService(service, m_statusUpdater);
}
StatusUpdatePolicy m_statusUpdater;
};