如何将模板参数传递给 CRTP?

Tav*_*son 5 c++ templates crtp

在下面的代码中:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢,

吉姆

Tem*_*Rex 5

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
};
Run Code Online (Sandbox Code Playgroud)

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

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
};
Run Code Online (Sandbox Code Playgroud)

要获得此接口的不同实现,只需定义不同的类,每个类都派生自FooInterface作为奇怪的重复模板参数

class FooImpl
:
    public FooInterface< FooImpl > 
{
private:
    // implementation
    friend class FooInterface< FooImpl > ;
    void do_foo() { std::cout << "Foo\n"; }
};

class AnotherFooImpl
:
    public FooInterface< AnotherFooImpl > 
{
private:
    // implementation
    friend class FooInterface< AnotherFooImpl >;
    void do_foo() { std::cout << "AnotherFoo\n"; }
};
Run Code Online (Sandbox Code Playgroud)

另一种方法是参数化接口的不同实现。这次,类模板同时依赖于模板-模板参数和非类型参数

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
};
Run Code Online (Sandbox Code Playgroud)

然后,实现是另一个类模板,它派生自接口,并以自身和非类型参数作为参数

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

这就是你如何称呼它们:

int main()
{
    FooImpl f1;         
    AnotherFooImpl f2;
    BarImpl< 1 > b1;
    BarImpl< 2 > b2;

    f1.foo();
    f2.foo();
    b1.bar();
    b2.bar();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

您问题中的课程不太适合这种一般模式。如果你可能想要提供Derived一些类似 CRTP 的行为,那么你可以这样做

class Derived1
: 
   public CRTP< Derived1 > 
{

};

template<int I>
class Derived2
: 
   public CRTPInt< Derived2, I >
{

};
Run Code Online (Sandbox Code Playgroud)

更新:根据/sf/answers/810026591/的讨论,我发现原始答案仅在 Visual Studio 2010 上编译,但由于一些 Microsoft 特定的、不可移植的功能而不能在 gcc 上编译。例如,self()函数 fromenable_down_cast是其派生类中的(模板)相关名称,因此在没有显式using指令的情况下不可见。此外,我还添加了具有适当保护级别的默认析构函数。最后,我将原来的类重命名enable_crtpenable_down_cast,因为这正是它的作用:手动启用静态多态性,编译器自动启用动态多态性。