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

Posted

技术标签:

【中文标题】如何将模板参数传递给 CRTP?【英文标题】:How do I pass template parameters to a CRTP? 【发布时间】:2012-07-18 16:53:22 【问题描述】:

在以下代码中:

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 以便可以传入模板化参数,然后将在派生定义中继续?

谢谢,

吉姆

【问题讨论】:

【参考方案1】:

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 << "Foo\n"; 
;

class AnotherFooImpl
:
    public FooInterface< AnotherFooImpl > 

private:
    // implementation
    friend class FooInterface< AnotherFooImpl >;
    void do_foo()  std::cout << "AnotherFoo\n"; 
;

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

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://***.com/a/11571808/819272 的讨论,我发现原始答案仅在 Visual Studio 2010 上编译,而不是在 gcc 上编译,因为某些 Microsoft 特定的、不可移植的功能。例如。来自enable_down_castself() 函数是其派生类中的(模板)依赖名称,因此如果没有明确的using 指令则不可见。此外,我添加了具有正确保护级别的默认析构函数。最后,我将原来的类 enable_crtp 重命名为 enable_down_cast,因为这正是它的作用:手动启用静态多态性,编译器自动启用动态多态性。

【讨论】:

我希望避免派生的 2 解决方案。我正在组合一个模块化相机对象,该对象允许策略混合和匹配镜头、小车等。我希望能够将变焦镜头指定为最小焦距和最大焦距,即 Camera>,但是镜头反过来需要访问相机才能调用其他模块的功能。 (实际上在镜头盒中没有,但在 Dollies and Heads 中却有。) @Tavison 对于基于策略的设计,您并不总是需要 CRTP。例如。您可以直接从每个策略派生:template&lt;typename ZoomPolicy, typename DolliesPolicy&gt; class Camera: public ZoomPolicy, pubic DolliesPlicy ; 相反,您可以让每个 ConcreteZoomPolicy 从特定的 ZoomInterface 等派生。 @Tavison 要继续,您可以创建template&lt;typename Impl, int Min, int Max&gt; class ZoomInterface: private enable_crpt&lt; Impl&lt;Min, Max&gt; &gt; ;,然后实现template&lt;int Min, int Max&gt; class ZoomImpl: public ZoomInterface&lt; ZoomImpl, Min, Max &gt; ;。然后你可以有一个Camera&lt; ZoomImpl&lt;24, 70&gt; &gt; 我会解决这个问题并在我得到它工作时接受你的回答。我知道我需要在其中再添加一层,但没有找到。 这不能在 VC10 中编译。我得到这些错误。错误 C2923:“F”:“X”不是参数“”的有效模板类型参数,请参阅“X”的声明,请参阅对类模板实例化“BarInterface”的引用正在编译错误 C3201 : 类模板“BarImpl”的模板参数列表与模板参数“F”的模板参数列表不匹配,请参阅正在编译的类模板实例化“BarImpl

以上是关于如何将模板参数传递给 CRTP?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 lambda 表达式作为参数传递给 c++ 模板

如何将三个或多个参数传递给自定义模板标签过滤器 django?

将模板参数传递给调用成员函数的函数

将可变参数函数模板参数传递给另一个函数

为啥我不能将函数指针作为模板参数传递给地图?

如何将参数传递给Vue组件并在组件模板中重新使用其值