如何将模板参数传递给CRTP

How do I pass template parameters to a CRTP?

本文关键字:参数传递 CRTP      更新时间:2023-10-16

在以下代码中:

template <typename T>
class CRTP
{
public:
};
template <int I, typename T>
class CRTPInt
{
public:
};
template <template <typename> class T>
class Derived : public T<Derived<T>>
{
public:
};
void main()
{
Derived<CRTP> foo;
Derived<CRTPInt<2>> foo2;
}

我如何编写CRPTInt,以便传入一个模板化的参数,然后在派生定义中继续该参数?

谢谢,

Jim

CRTP模式通常用于启用静态多态性混合(参数化)行为的能力。为了说明两种替代方案,可以方便地首先定义一个通用模板

template
<
        typename Derived
>
class enable_down_cast
{
private:
        // typedefs
        typedef enable_down_cast Base;
public:
        Derived const* self() const
        {
                // casting "down" the inheritance hierarchy
                return static_cast<Derived const*>(this);
        }
        // write the non-const version in terms of the const version
        // Effective C++ 3rd ed., Item 3 (p. 24-25)
        Derived* self()
        {
                return const_cast<Derived*>(static_cast<Base const*>(this)->self());
        }
protected:
        // disable deletion of Derived* through Base*
        // enable deletion of Base* through Derived*
        ~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};

然后为您想要的行为类型定义一个接口类模板

template<typename FX>
class FooInterface
:
    // enable static polymorphism
    public enable_down_cast< FX >
{
private:
    // dependent name now in scope
    using enable_down_cast< FX >::self;
public:
    // interface
    void foo() { self()->do_foo(); }
protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~IFooInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};

要获得该接口的不同实现,只需定义不同的类,每个类都从FooInterface派生,并将其自身定义为奇怪地重复出现的模板参数

class FooImpl
:
    public FooInterface< FooImpl > 
{
private:
    // implementation
    friend class FooInterface< FooImpl > ;
    void do_foo() { std::cout << "Foon"; }
};
class AnotherFooImpl
:
    public FooInterface< AnotherFooImpl > 
{
private:
    // implementation
    friend class FooInterface< AnotherFooImpl >;
    void do_foo() { std::cout << "AnotherFoon"; }
};

另一种选择是对接口的不同实现进行参数化。这一次,类模板依赖于模板模板参数和非类型参数

template<template<int> class F, int X>
class BarInterface
:
    public enable_down_cast< F<X> >
{
private:
    // dependent name now in scope
    using enable_down_cast< F<X> >::self;
public:
    // interface
    void bar() { self()->do_bar(); }    
protected:
    // disable deletion of Derived* through Base*
    // enable deletion of Base* through Derived*
    ~BarInterface() = default; // C++11 only, use ~BarInterface() {} in C++98/03
};

然后,实现是另一个类模板,它从接口派生,其本身和非类型参数都作为参数

template< int X >
class BarImpl
:
    public BarInterface< BarImpl, X > 
{
private:
    // implementation
    friend class BarInterface< ::BarImpl, X >;
    void do_bar() { std::cout << X << "n"; }    
};

这就是你对它们的称呼:

int main()
{
    FooImpl f1;         
    AnotherFooImpl f2;
    BarImpl< 1 > b1;
    BarImpl< 2 > b2;
    f1.foo();
    f2.foo();
    b1.bar();
    b2.bar();
    return 0;
}

你问题中的类不太符合这种一般模式。如果你可能想给Derived一些类似CRTP的行为,那么你可以做

class Derived1
: 
   public CRTP< Derived1 > 
{
};
template<int I>
class Derived2
: 
   public CRTPInt< Derived2, I >
{
};

更新:基于https://stackoverflow.com/a/11571808/819272,我发现最初的答案只在Visual Studio 2010上编译,但由于一些特定于Microsoft的不可移植功能,所以没有在gcc上编译。例如,enable_down_cast中的self()函数在其派生类中是一个依赖于(模板)的名称,因此在没有显式using指令的情况下是不可见的。此外,我添加了具有适当保护级别的默认析构函数。最后,我将原来的类enable_crtp重命名为enable_down_cast,因为这正是它的作用:手动启用静态多态性,就像编译器自动启用动态多态性一样。