选择要从中派生的 CRTP 基类

Selecting which CRTP base class to derive from

本文关键字:CRTP 基类 派生 选择      更新时间:2023-10-16

假设我有以下非常简单的CRTP基类:

template< class D, class T >
struct Base
{
    T foo() 
    {   
        return static_cast< D* >(this)->foo_i();
    }
};

此外,还有一些派生类。一切运行良好,但存在一个问题:有一种特殊情况(或者可能是一对),我真的非常非常希望其中两个类具有运行时多态行为(需要将它们放在容器中)。换句话说,我希望一些派生 CRTP 类也具有虚拟版本。所以,我想出了以下类:

template< class T >
struct VirtualBase : public Base< VirtualBase< T >, T >
{
    virtual T foo_i() = 0;
};

现在,在我需要运行时多态性的地方,我只是从这个类派生。假设我希望我的派生类DerivedB有一个虚拟版本。原版 DerivedB 看起来像这样:

template< class T >
struct DerivedB : public Base< DerivedB< T >, T >
{
    T foo_i() 
    { 
        std::cout << "I'm special!n";
        return T(); 
    }
};

我想做的本质上是向这个类添加一个额外的模板参数,以便我可以在编译时选择是从 Base 派生的(如果我想要模拟的"动态"绑定)还是 VirtualBase(如果我想要真正的动态绑定)。类似于以下伪C++:

template< class B, class T >
struct DerivedB : public B< DerivedB< T >, T >
{
    T foo_i() 
    { 
        std::cout << "I'm special!n";
        return T(); 
    }
};

因此,对于普通 CRTP,将Base传递为B,对于虚拟类,VirtualBase传递为B。当然,问题在于它们采用不同数量的参数(Base需要派生类的类型),我无法提出可行的解决方案。

那么,如何在编译时选择基类呢?或者,如果这太复杂/不可能,那么拥有类的静态(CRTP)和动态(虚拟)版本的最简单方法是什么,否则实现是相同的?

最简单的方法可能就是添加"class D"作为VirtualBase的未使用模板参数,使其符合相同的接口。

如果无法更改 VirtualBase,可以使用中间模板:

template <class D, class T> class VirtualBaseWrapper : public VirtualBase<T>{}