CRTP 特征仅适用于模板派生类

Posted

技术标签:

【中文标题】CRTP 特征仅适用于模板派生类【英文标题】:CRTP traits only working with templated derived class 【发布时间】:2019-03-20 10:27:12 【问题描述】:

我看到了一个在 CRTP 模式的基类中使用派生类型特征的习惯用法,如下所示:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base 
    using size_type = typename traits<Derived>::size_type;
;

template <typename T>
struct Derived1 : Base<Derived1<T>>
    using size_type = size_t;
    void print() std::cout << "Derived1" << std::endl; 
;

template <typename T>
struct traits<Derived1<T>> 
    using size_type = size_t;
;

int main()

    using T = float;
    Derived1<T> d1;
    d1.print();

我的理解是,这个成语的目的是为了延迟Base类的size_type的实例化。我感到困惑的是,这种模式似乎只有在派生类本身是模板化的情况下才有效。例如,如果我们将代码更改为:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base 
    using size_type = typename traits<Derived>::size_type;
;

struct Derived1 : Base<Derived1>
    using size_type = size_t;
    void print() std::cout << "Derived1" << std::endl; 
;

template <>
struct traits<Derived1> 
    using size_type = size_t;
;

int main()

    Derived1 d1;
    d1.print();

然后我们得到错误

prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19:   required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
     using size_type = typename traits<Derived>::size_type;
                                                          ^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
 struct traits;
        ^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
         Derived1<float> d1;

谁能给我一个解释,说明为什么模板化的派生类可以编译,但非模板化的类不能?

【问题讨论】:

"我的理解是这个成语的目的是延迟 Base 类的 size_type 的实例化" 不,CRTP 的目的是将信息注入到一个类中,例如编译时多态可用。我相信你试图做的是滥用 CRTP。您正处于实例化时间模板的副作用中,与 CRTP 完全无关。 【参考方案1】:

您看到的问题与 CRTP 无关。

这是标准提到的内容。

如果在实例化点 (13.7.4.1) 已声明类模板但未定义类模板, 实例化产生一个不完整的类类型(6.7)。 [示例:

template<class T> class X; X<char> ch; // error: incomplete type
X<char>

您的traits 仅在Base&lt;Derived&gt; 的实例化点被声明,因此根据标准(参见上面从标准中提取的内容),struct traits&lt;Derived&gt; 产生一个不完整的类型。

您应该重新排序代码,以便在 Base&lt;Derived&gt; 被实例化时看到 traits&lt;Derived&gt; 特化。

【讨论】:

【参考方案2】:

您看到的编译错误与 CRTP 无关,它只是一些依赖项的混杂。

在没有模板的代码中,您的“Base”结构需要专门的“traits”结构的定义,但它只在之后出现,因此它尝试使用它在上面的声明中看到的不完整类型。

要使代码正常工作,您需要在 Base 声明之前具有“特征”特化,这需要您还添加 Derived 1 的声明,这是一个编译代码:

class Derived1;

template<typename Derived>
struct traits;

template <>
struct traits<Derived1> 
    using size_type = size_t;
;

template<typename Derived>
struct Base 
    using size_type = typename traits<Derived>::size_type;
;

struct Derived1 : Base<Derived1>
    using size_type = size_t;
    void print() std::cout << "Derived1" << std::endl; 
;


int main()

    Derived1 d1;
    d1.print();

【讨论】:

以上是关于CRTP 特征仅适用于模板派生类的主要内容,如果未能解决你的问题,请参考以下文章

通过指向base,static_cast,crtp,删除模板的指针派生的成员

派生模板类对象的实例化

CRTP:根据派生类内容启用基类中的方法

c++派生类的类型列表

使用奇怪的重复模板模式 (CRTP) 在抽象基类中实现赋值运算符

CRTP 中间类也需要成为 final