如何将虚拟调用从未模板化的父级干净地分派到模板化的子级

how to cleanly dispatch virtual calls from non templated parent to templated child

本文关键字:分派 调用 虚拟      更新时间:2023-10-16

我有一个非常丑陋的实现,我想重构它,但我不确定如何重构。这是我所拥有的:

一些模板化对象

template< typename T>
class Thing { };

接口

class IService {
     public:
          virtual void func(void* arg) = 0;
};

一个实现它的模板类

template< typename T>
class Service< T> : public IService {
     virtual void func(void* arg) {
          func((Thing< T>* )arg);
     }
     void func(Thing< T>* arg) {
          // do something with arg
     }
};

原因是我想要一个可以传递的非模板化的IService。模板化IService将导致通过大型代码库进行大规模重构。

因此,我实例化Service<T> 对象,传递IService,并使用模板化的Thing对象调用func,这会对采用实际Thing对象的类似函数进行难看的强制转换。有没有什么干净的方法可以在不模板化IService的情况下实现这一点?

编辑:更多上下文。这就是我真正想要的:

template<typename T>
class IService {
     public:
          virtual void func(Thing<T>* arg) = 0;
};

但是,我无法对IService进行模板化。

假设你不想或不能给Thing一个基类,并且给定的Service<T>只能处理具有相同TThing,你可以这样做:

class IService {
     public:
        template <typename T>
        void func(Thing<T>* arg) {
            auto self = dynamic_cast<Service<T>*>(this);
            if (self) {
                self->func(arg);
            } else {
                // decide how to handle this error
            }
        }
};

func将不是虚拟的。这只是一个从IService派生的模板策略,需要有一个func(Thing<T>*)方法。

也许Thing的一些接口可以做到这一点?

class IThing { };
template< typename T>
class Thing: public IThing { };
class IService {
     public:
          virtual void func(IThing* arg) = 0;
};
template< typename T>
class Service: public IService {
    virtual void func(IThing* arg) {
        Thing<T> *thing = dynamic_cast<Thing<T> *>(arg);
        if (thing) {
            // implementation
        }
    }
};

我假设您希望通过使用非模板化的IService类来键入erase Thing<T>模板参数,并且在IService的实现中不关心T。我不确定这些假设是否正确,但这里有一个可能的解决方案。

我还假设在Thing中调度virtual在您的用例中就足够了。

// Create a `ThingBase` class to "type-erase" `Thing`'s 
// template parameter. `ThingBase` should contain your
// `func`, as `virtual`.
struct ThingBase
{
    virtual void func();
};
// Your `Thing<T>` class should override `func`
template <typename T>
struct Thing : ThingBase
{
    void func() override { /* ... */ }
};
// Your `IService` now only needs to be as follows:
struct IService
{
    void func(ThingBase& x) 
    {
        x.func();
    }
};