覆盖可变参数 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{});
}

此处 SubscriberServiceType... 中的每个模板参数都有一个纯虚函数 handleService(ServiceType const)。所以我必须在 StatusUpdater 上覆盖每一个。在这里,我已经为 IntServiceFloatService 手动提供了我需要的那些,我知道在这个最小的例子中我只需要那些。但我希望能够为包 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;
};

[Live example]

我看不到完全符合您要求的解决方案。但是,您可以在根本不需要 virtuality 的情况下实现相同的行为。我最初想到了一个 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;
};