关于C++:CRTP与多级继承

CRTP and multilevel inheritance

我的一个朋友问我"如何使用CRTP来取代多层次遗传中的多态性"。更准确地说,在这种情况下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A {

  void bar() {
    // do something and then call foo (possibly) in the derived class:
    foo();
  }

  // possibly non pure virtual
  virtual void foo() const = 0;
}

struct B : A {
  void foo() const override { /* do something */ }
}

struct C : B {
  // possibly absent to not override B::foo().
  void foo() const final { /* do something else */ }
}

我和我的朋友知道,CRTP并不是多态性的替代品,但我们对两种模式都可以使用的情况很感兴趣。(为了这个问题,我们对每种模式的利弊都不感兴趣。)

  • 以前有人问过这个问题,但结果证明,作者希望实现命名参数惯用法,他自己的答案更多地关注这个问题,而不是CRTP。另一方面,最有投票权的答案似乎只是一个派生类方法,它在基类中调用了它的同名项。

  • 我想出了一个答案(张贴在下面),它有很多样板代码,我想知道是否有更简单的选择。


  • (1)层次结构中最顶层的类如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    template <typename T>
    class A {

    public:

      void bar() const {
        // do something and then call foo (possibly) in the derived class:
        foo();
      }

      void foo() const {
        static_cast<const T*>(this)->foo();
      }

    protected:

      ~A() = default;

      // Constructors should be protected as well.

    };

    A::foo()的行为类似于纯虚拟方法,因为它没有"默认实现",调用被定向到派生类。然而,这并不能阻止A被实例化为非基类。为了得到这种行为,我们把A::~A()改为protected

    备注:不幸的是,当使用= default;时,gcc bug将特殊成员函数公开。在这种情况下,应该使用

    1
    2
    protected:
        ~A() {}

    但是,对于调用构造函数与调用析构函数不匹配的情况(这可能通过operator new发生),保护析构函数还不够。因此,建议同时保护所有构造函数(包括复制和移动构造函数)。

    当允许实例化A并且A::foo()的行为类似于非纯虚拟方法时,那么A应该类似于下面的模板类B

    (2)层次结构中间的类(或上面段落中解释的最上面的类)如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    template <typename T = void>
    class B : public A<B<T>> { // no inherinace if this is the topmost class

    public:

      // Constructors and destructor

      // boilerplate code :-(
      void foo() const {
        foo_impl(std::is_same<T, void>{});
      }

    private:

      void foo_impl(std::true_type) const {
        std::cout <<"B::foo()
    "
    ;
      }

      // boilerplate code :-(
      void foo_impl(std::false_type) const {
        if (&B::foo == &T::foo)
          foo_impl(std::true_type{});
        else
          static_cast<const T*>(this)->foo();
      }

    };

    构造函数和析构函数是公共的,T默认为void。这允许B<>类型的对象是层次结构中派生最多的对象,并使其合法:

    1
    2
    B<> b;
    b.foo();

    注意,B::foo()表现为非纯虚拟方法,如果B是最派生的类(或者更准确地说,如果Tvoid的话),那么b.foo();称为"foo()的默认实现"(输出B::foo())。如果T不是void,那么调用将被定向到派生类。这是通过标签调度完成的。

    测试&B::foo == &T::foo需要避免无限递归调用。实际上,如果派生类T不重新实现foo(),则调用static_cast(this)->foo();将解析为B::foo(),后者再次调用B::foo_impl(std::false_type)。此外,这个测试可以在编译时解决,代码可以是if (true)if (false),优化器可以完全删除测试(例如gcc和-o3)。

    (3)最后,层次结构的底部如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class C : public B<C> {

    public:

      void foo() const {
        std::cout <<"C::foo()
    "
    ;
      }

    };

    或者,如果继承的实现(B::foo()足够的话,可以完全删除C::foo()

    注意,C::foo()与final方法类似,因为调用它不会将调用重定向到派生类(如果有)。(为了使其成为非最终类,应该使用像B这样的模板类。)

    (4)也见:

    如何在使用CRTP时避免错误?


    注:这不是"最终覆盖"问题的具体解决方案,而是一般的CRTP多层次继承问题(因为我在任何地方都没有找到解决方法,我认为我的发现会很有用)。

    编辑:我在这里发布了最终覆盖问题的解决方案

    我最近了解到crtp及其作为运行时多态性静态替换的潜力。在搜索了一段时间后,我发现crtp是否可以作为一个类似的多态性"插入"替代品,这样你就可以使用多层次的继承等等,我不得不说,我很惊讶地发现,在没有样板的情况下,我找不到一个合适的通用解决方案,样板可以无限扩展。毕竟,考虑到CRTP的所有性能优势,为什么不尝试让它成为多态性的替代品呢?接下来进行了一些调查,我想说的是:

    问题:

    经典的CRTP模式在CRTP接口和实现类之间创建了一个可访问性的"循环"。(crtp接口类通过自身对模板参数类型的静态强制转换访问"base"实现类,实现类从crtp接口类继承公共接口。)创建具体实现时,将关闭循环,使从具体imp继承非常困难。元素作用类,使得从它派生的东西也以多态的方式表现。

    经典的CRTP单级继承

    解决方案:

    将模式分为三个概念:

    • "抽象接口类",即CRTP接口。
    • "可继承的实现类",可由其他可继承的实现类无限期继承。
    • "具体类",它将抽象接口与所需的可继承实现类结合在一起,并关闭循环。

    下面是一个图表来帮助说明:

    CRTP的多级继承

    技巧是将具体的实现类作为模板参数一直传递到所有可继承实现类中的抽象接口类中。

    通过这种方法,您可以:

  • 无限期地继承实现,
  • 从任何级别调用CRTP多级继承链中实现的最高方法,
  • 以层次不可知的方式设计每个实现,
  • 忘了使用样板代码(好吧,不超过经典的单级CRTP)。
  • 它完美地反映了虚拟/运行时多态性。

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    #include <iostream>

    template <class Top>
    struct CrtpInterface
    {
      void foo()
      {
        std::cout <<"Calling CrtpInterface::foo()
    "
    ;
        fooImpl();
      }
      void foo2()
      {
        std::cout <<"Calling CrtpInterface::foo2()
    "
    ;
        fooImpl2();
      }
      void foo3()
      {
        std::cout <<"Calling CrtpInterface::foo3()
    "
    ;
        fooImpl3();
      }
      void foo4()
      {
        std::cout <<"Calling CrtpInterface::foo4()
    "
    ;
        fooImpl4();
      }

    // The"pure virtual functions"
    protected:
      inline void fooImpl()
      {
        top().fooImpl();
      }
      inline void fooImpl2()
      {
        top().fooImpl2();
      }
      inline void fooImpl3()
      {
        top().fooImpl3();
      }
      inline void fooImpl4()
      {
        top().fooImpl4();
      }
      inline Top& top()
      {
        return static_cast<Top&>(*this);
      }
    };

    template<class Top>
    class DefaultImpl : public CrtpInterface<Top>
    {
      using impl = CrtpInterface<Top>;
      friend impl;

      void fooImpl()
      {
        std::cout <<"Default::fooImpl()
    "
    ;
      }

      void fooImpl2()
      {
        std::cout <<"Default::fooImpl2()
    "
    ;
        std::cout <<"Calling foo() from interface
    "
    ;
        impl::foo();
      }

      void fooImpl3()
      {
        std::cout <<"Default::fooImpl3()
    "
    ;
        std::cout <<"Calling highest level fooImpl2() from interface
    "
    ;
        impl::fooImpl2();
      }

      void fooImpl4()
      {
        std::cout <<"Default::fooImpl4()
    "
    ;
        std::cout <<"Calling highest level fooImpl3() from interface
    "
    ;
        impl::fooImpl3();
      }
    };

    template<class Top>
    class AImpl : public DefaultImpl<Top>
    {
      using impl = CrtpInterface<Top>;
      friend impl;

      void fooImpl()
      {
        std::cout <<"A::fooImpl()
    "
    ;
      }
    };

    struct A : AImpl<A>
    {
    };

    template<class Top>
    class BImpl : public AImpl<Top>
    {
      using impl = CrtpInterface<Top>;
      friend impl;

      protected:
        BImpl()
          : i{1}
        {
        }

      private:
        int i;
        void fooImpl2()
        {
          std::cout <<"B::fooImpl2():" << i <<"
    "
    ;
        }
    };

    struct B : BImpl
    {
    };

    template<class Top>
    class CImpl : public BImpl<Top>
    {
      using impl = CrtpInterface<Top>;
      friend impl;

      protected:
        CImpl(int x = 2)
          : i{x}
        {
        }

      private:
        int i;
        void fooImpl3()
        {
          std::cout <<"C::fooImpl3():" << i <<"
    "
    ;
        }
    };

    struct C : CImpl<C>
    {
      C(int i = 9)
        : CImpl(i)
      {
      }
    };

    template<class Top>
    class DImpl : public CImpl<Top>
    {
      using impl = CrtpInterface<Top>;
      friend impl;

      void fooImpl4()
      {
        std::cout <<"D::fooImpl4()
    "
    ;
      }
    };

    struct D : DImpl<D>
    {
    };

    int main()
    {
      std::cout <<"### A ###
    "
    ;
      A a;
      a.foo();
      a.foo2();
      a.foo3();
      a.foo4();

      std::cout <<"### B ###
    "
    ;
      B b;
      b.foo();
      b.foo2();
      b.foo3();
      b.foo4();

      std::cout <<"### C ###
    "
    ;
      C c;
      c.foo();
      c.foo2();
      c.foo3();
      c.foo4();

      std::cout <<"### D ###
    "
    ;
      D d;
      d.foo();
      d.foo2();
      d.foo3();
      d.foo4();
    }

    哪些印刷品:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    ### A ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo3()
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    ### B ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    B::fooImpl2(): 1
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    B::fooImpl2(): 1
    ### C ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    C::fooImpl3(): 9
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    C::fooImpl3(): 9
    ### D ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    C::fooImpl3(): 2
    Calling CrtpInterface::foo4()
    D::fooImpl4()

    使用这种方法和"variant-style"包装器(使用一些sechsy variandic模板和宏构建,也许稍后我会发布它),它就像一个指向虚拟抽象基类的指针,我能够有效地创建继承自同一接口的crtp类的向量。

    我将性能与like虚拟类的like向量进行了比较,所有这些都基于等效的虚拟接口,我发现使用这种方法,根据场景的不同,我可以将性能提高8倍!这是非常令人鼓舞的,因为生成功能多态的CRTP类层次结构所需的开销相对较少!


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    template<class Derived>
    struct A {
      void foo() {
        static_cast<Derived*>(this)->foo();
      }
    };

    template<class Derived>
    struct B: A <Derived> {
      void foo() {
        // do something
      }
    };

    struct C: B <C> {
      void foo(); // can be either present or absent
    };

    如果缺少c中的foo(),将调用b中的foo()。否则,B中的将被覆盖。


    当我意识到我最初的答案实际上并没有处理手头上的最后一个覆盖问题后,我想我应该再加一个。我想以与我以前的回答类似的方式提出一个"最终覆盖"解决方案。

    问题:

    crtp接口类总是通过静态强制转换重定向到最高派生类。这与"final"函数的概念是不一致的:如果所需的"final"函数没有在最高派生类上实现,并且被更高的类"重写"(因为您不能给函数"final"属性,除非它是虚拟的,这是我们在crtp中试图避免的),crtp接口将重定向到所需的"final"。除了"超控"功能。

    解决方案:

    将接口分为三个概念:

    • 不带任何重定向函数的抽象接口类,它继承:
    • 一个抽象重定向类,其重定向函数重定向到最高派生类,除非一个或多个重定向函数被以下对象覆盖:
    • 一个具体的"重定向覆盖"类,它用一个实现覆盖重定向函数。

    在实例化具体实现类时,我们不将具体实现类作为模板参数通过所有"可继承实现类"传入接口,而是将接口将从中继承的重定向类作为模板参数传递。

    当我们想要使一个函数成为"final"时,我们只需创建一个从抽象重定向类继承的"redirect override class",并重写我们想要使其成为final的重定向函数。然后,我们将这个新的"重定向覆盖类"作为参数传递给所有可继承的实现类。

    采用这种方法:

  • 直接调用"final"函数,而不是通过强制转换进行重定向(除非需要在可继承实现类而不是重定向重写类中实现"final"函数)。
  • "final"函数不能被任何未来的用户代码覆盖,
  • 每个"final"函数只需要一个额外的implfinal类,每个继承级别都不需要额外的样板文件。
  • 这听起来很复杂,所以这里有一个流程图,我试图让事情更容易理解:

    dimpl和eimpl具有最终函数,当dimpl或eimpl从以下对象继承时,不能重写这些函数:></P><P>示例代码:</P></p>
<div class=

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    #include <iostream>
    #include <type_traits>

    template <class Top>
    struct Redirect
    {
    protected:
      // The"pure virtual functions"
      inline void fooImpl()
      {
        top().fooImpl();
      }
      inline void fooImpl2()
      {
        top().fooImpl2();
      }
      inline void fooImpl3()
      {
        top().fooImpl3();
      }
      inline void fooImpl4()
      {
        top().fooImpl4();
      }
      inline Top& top()
      {
        // GCC doesn't allow static_cast<Top&>(*this)
        // since Interface uses private inheritance
        static_assert(std::is_base_of<Redirect, Top>::value,"Invalid Top class specified.");
        return (Top&)(*this);
      }
    };

    // Wraps R around the inner level of a template T, e.g:
    // R := Redirect, T := X, then inject_type::type := Redirect<X>
    // R := Redirect, T := A<B<C<X>>>, then inject_type::type := A<B<C<Redirect<X>>>>
    template<template<class> class R, class T>
    struct inject_type
    {
      using type = R<T>;
    };

    template<template<class> class R, class InnerFirst, class... InnerRest, template<class...> class Outer>
    struct inject_type<R, Outer<InnerFirst, InnerRest...>>
    {
      using type = Outer<typename inject_type<R, InnerFirst>::type, InnerRest...>;
    };

    // We will be inheriting either Redirect<...> or something
    // which derives from it (and overrides the functions).
    // Use private inheritance, so that all polymorphic calls can
    // only go through this class (which makes it impossible to
    // subvert redirect overrides using future user code).
    template <class V>
    struct Interface : private inject_type<Redirect, V>::type
    {
      using impl = Interface;

      void foo()
      {
        std::cout <<"Calling Interface::foo()
    "
    ;
        fooImpl();
      }

      void foo2()
      {
        std::cout <<"Calling Interface::foo2()
    "
    ;
        fooImpl2();
      }

      void foo3()
      {
        std::cout <<"Calling Interface::foo3()
    "
    ;
        fooImpl3();
      }

      void foo4()
      {
        std::cout <<"Calling Interface::foo4()
    "
    ;
        fooImpl4();
      }

    private:
      using R = typename inject_type<::Redirect, V>::type;

    protected:
      using R::fooImpl;
      using R::fooImpl2;
      using R::fooImpl3;
      using R::fooImpl4;
    };

    template<class V>
    struct DefaultImpl : Interface<V>
    {
      template<class>
      friend struct Redirect;

    protected:
      // Picking up typename impl from Interface, where all polymorphic calls must pass through
      using impl = typename DefaultImpl::impl;

      void fooImpl()
      {
        std::cout <<"Default::fooImpl()
    "
    ;
      }

      void fooImpl2()
      {
        std::cout <<"Default::fooImpl2()
    "
    ;
        std::cout <<"Calling foo() from interface
    "
    ;
        impl::foo();
      }

      void fooImpl3()
      {
        std::cout <<"Default::fooImpl3()
    "
    ;
        std::cout <<"Calling highest level fooImpl2() from interface
    "
    ;
        impl::fooImpl2();
      }

      void fooImpl4()
      {
        std::cout <<"Default::fooImpl4()
    "
    ;
        std::cout <<"Calling highest level fooImpl3() from interface
    "
    ;
        impl::fooImpl3();
      }
    };

    template<class V>
    struct AImpl : public DefaultImpl<V>
    {
      template<class>
      friend struct Redirect;

    protected:
      void fooImpl()
      {
        std::cout <<"A::fooImpl()
    "
    ;
      }
    };

    struct A : AImpl<A>
    {
    };

    template<class V>
    struct BImpl : public AImpl<V>
    {
      template<class>
      friend struct Redirect;

    protected:
      BImpl()
        : i{1}
      {
      }

    private:
      int i;
      void fooImpl2()
      {
        std::cout <<"B::fooImpl2():" << i <<"
    "
    ;
      }
    };

    struct B : BImpl
    {
    };

    template<class V>
    struct CImpl : public BImpl<V>
    {
      template<class>
      friend struct Redirect;

      protected:
        CImpl(int x = 2)
          : i{x}
        {
        }

      private:
        int i;
        void fooImpl3()
        {
          std::cout <<"C::fooImpl3():" << i <<"
    "
    ;
        }
    };

    struct C : CImpl<C>
    {
      C(int i = 9)
        : CImpl(i)
      {
      }
    };

    // Make D::fooImpl4 final
    template<class V>
    struct DImplFinal : public V
    {
    protected:
      void fooImpl4()
      {
        std::cout <<"DImplFinal::fooImpl4()
    "
    ;
      }
    };


    // Wrapping V with DImplFinal overrides the redirecting functions
    template<class V>
    struct DImpl : CImpl<DImplFinal<V>>
    {
    };

    struct D : DImpl<D>
    {
    };

    template<class V>
    struct EImpl : DImpl<V>
    {
      template<class>
      friend struct Redirect;

    protected:
      void fooImpl()
      {
        std::cout <<"E::fooImpl()
    "
    ;
      }

      void fooImpl3()
      {
        std::cout <<"E::fooImpl3()
    "
    ;
      }

      // This will never be called, because fooImpl4 is final in DImpl
      void fooImpl4()
      {
        std::cout <<"E::fooImpl4(): this should never be printed
    "
    ;
      }
    };

    struct E : EImpl<E>
    {
    };

    // Make F::fooImpl3 final
    template<class V, class Top>
    struct FImplFinal : public V
    {
    protected:
      // This is implemented in FImpl, so redirect
      void fooImpl3()
      {
        top().fooImpl3();
      }

      // This will never be called, because fooImpl4 is final in DImpl
      void fooImpl4()
      {
        std::cout <<"FImplFinal::fooImpl4() this should never be printed
    "
    ;
      }

      inline Top& top()
      {
        // GCC won't do a static_cast directly :(
        static_assert(std::is_base_of<FImplFinal, Top>::value,"Invalid Top class specified");
        return (Top&)(*this);  
      }
    };

    // Wrapping V with FImplFinal overrides the redirecting functions, but only if they haven't been overridden already
    template<class V>
    struct FImpl : EImpl<FImplFinal<V, FImpl<V>>>
    {
      template<class>
      friend struct Redirect;
      template<class, class>
      friend struct FImplFinal;

    protected:
      FImpl()
        : i{99}
      {
      }

      // Picking up typename impl from DefaultImpl
      using impl = typename FImpl::impl;

    private:
      int i;

      void fooImpl2()
      {
        std::cout <<"F::fooImpl2()
    "
    ;
        // This will only call DFinal::fooImpl4();
        std::cout <<"Calling fooImpl4() polymorphically. (Should not print FImplFinal::fooImpl4() or EImpl::fooImpl4())
    "
    ;
        impl::fooImpl4();
      }

      void fooImpl3()
      {
        std::cout <<"FImpl::fooImpl3(), i =" << i << '
    '
    ;
      }
    };

    struct F : FImpl<F>
    {
    };

    int main()
    {
      std::cout <<"### A ###
    "
    ;
      A a;
      a.foo();
      a.foo2();
      a.foo3();
      a.foo4();

      std::cout <<"### B ###
    "
    ;
      B b;
      b.foo();
      b.foo2();
      b.foo3();
      b.foo4();

      std::cout <<"### C ###
    "
    ;
      C c;
      c.foo();
      c.foo2();
      c.foo3();
      c.foo4();

      std::cout <<"### D ###
    "
    ;
      D d;
      d.foo();
      d.foo2();
      d.foo3();
      d.foo4();

      std::cout <<"### E ###
    "
    ;
      E e;
      e.foo();
      e.foo2();
      e.foo3();
      e.foo4();

      std::cout <<"### F ###
    "
    ;
      F f;
      f.foo();
      f.foo2();
      f.foo3();
      f.foo4();
    }

    代码打印:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    ### A ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo3()
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    Default::fooImpl2()
    Calling foo() from interface
    Calling CrtpInterface::foo()
    A::fooImpl()
    ### B ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    B::fooImpl2(): 1
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    Default::fooImpl3()
    Calling highest level fooImpl2() from interface
    B::fooImpl2(): 1
    ### C ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    C::fooImpl3(): 9
    Calling CrtpInterface::foo4()
    Default::fooImpl4()
    Calling highest level fooImpl3() from interface
    C::fooImpl3(): 9
    ### D ###
    Calling CrtpInterface::foo()
    A::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    C::fooImpl3(): 2
    Calling CrtpInterface::foo4()
    DImplFinal::fooImpl4()
    ### E ###
    Calling CrtpInterface::foo()
    E::fooImpl()
    Calling CrtpInterface::foo2()
    B::fooImpl2(): 1
    Calling CrtpInterface::foo3()
    E::fooImpl3()
    Calling CrtpInterface::foo4()
    DImplFinal::fooImpl4()
    ### F ###
    Calling CrtpInterface::foo()
    E::fooImpl()
    Calling CrtpInterface::foo2()
    F::fooImpl2()
    Attempting to call FFinal::fooImpl4() or E::fooImpl4()
    DImplFinal::fooImpl4()
    Calling CrtpInterface::foo3()
    FImpl::fooImpl3(), i = 99
    Calling CrtpInterface::foo4()
    DImplFinal::fooImpl4()

    多层次继承不是问题,但是CRTP是如何创建多态性的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    template<typename Derived>
    struct Base
    {
        void f() { /* Basic case */ }

        //"Pure virtual" method
        void pure() { static_cast<Derived*>(this)->pure(); }
    };

    struct Overriding: Base<Overriding>
    {
        void f() { /* Special case */ }

        // This method must exists to prevent endless recursion in Base::f
        void pure() { /* ... */ }
    };

    struct NonOverriding: Base<NonOverriding>
    {
        void pure() { /* ... */ }
    };

    template<typename Derived>
    void f(const Base<Derived>& base)
    {
        base.f();    // Base::f
        base.pure(); // Base::pure, which eventually calls Derived::pure

        // Derived::f if an overriding method exists.
        // Base::f otherwise.
        static_cast<const Derived&>(base).f();
    }

    还可以引入derived方法,以避免在每次调用时进行冗长的类型转换。

    1
    2
    3
    4
    5
    6
    template<typename Derived>
    struct Base
    {
        Derived& derived() { return *static_cast<Derived*>(this); }
        const Derived& derived() const { return *static_cast<const Derived*>(this); }
    };

    在这个线程中有很多事情正在进行,但我发现它们没有用处,所以我在这里分享我自己解决这个问题的方法。

    CRTP主要是一种代码缩减模式。为了正确地工作,有必要在继承层次的每一个层次上,都能够从下面的层次调用所有函数——就像在通常的动态继承中一样。

    但是,在crtp中,每个阶段都必须进一步了解当前从中派生的最终类型,因为最终这是crtp的全部要点——调用始终应用于当前(静态)最终类型的函数。

    可以通过在静态继承层次结构的每个级别添加一个间接层来实现这一点,如下例所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    template<typename derived_t>
    struct level0_impl
    {
        auto const& derived() const
        {
            return static_cast<derived_t const&>(*this);
        }
    };
    struct level0 : public level0_impl<level0>
    {
        using level0_impl<level0>::level0_impl;
    };


    template<typename derived_t>
    struct level1_impl : level0_impl<derived_t>
    {
        auto only_for_level1_and_derived() const
        {
            return derived().foo;
        };
        auto do_something() const { std::cout<<"hi"<<std::endl; }
    };
    struct level1 : public level1_impl<level1>
    {
        using level1_impl<level1>::level1_impl;
    };


    template<typename derived_t>
    struct level2_impl : public level1_impl<derived_t>
    {
        auto only_for_level2_and_derived() const
        {
            return derived().bar;
        };
    };
    struct level2 : public level2_impl<level2>
    {
        using level2_impl<level2>::level2_impl;
    };

    // ... and so on ...

    可以将其与最终类型一起使用,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    struct final : public level2_impl<final>
    {
        int foo = 1;
        double bar = 2.0;
    };

    int main()
    {
        final f{};
        std::cout<< f.only_for_level1_and_derived() <<std::endl;   //prints variable foo = 1
        std::cout<< f.only_for_level2_and_derived() <<std::endl;   //prints variable bar = 2.0
    }

    或者,只需删除_impl后缀即可使用每个级别:

    1
    level1{}.do_something();   //prints"hi"

    这是一件好事,它显然不适用于此线程中的其他方法,例如

    1
    2
    3
    4
    5
    template<typename T> class A { auto& derived() {return static_cast<T&>(*this);} };
    template<typename T> class B : A<B<T> > {};
    template<typename T> class C : B<C> {};    //here derived() in the base class does
                                               //not return C, but B<C> -- which is
                                               //not what one usually wants in CRTP


    这里有一个可能的实现,它可以减少类内的样板代码(但不能减少辅助代码的总量)。

    这个解决方案的思想是使用sfinae和重载来选择impl函数。

    (i)甲级

    1
    2
    3
    4
    5
    template <typename T> class A {
       void foo() const {
        static_cast<const T*>(this)->foo( Type2Type<T> );
       }
     }

    其中typetotype是模板结构

    1
    2
    3
    template< typename T > struct Type2Type {
      typedef T OriginalType
    }

    这对于帮助编译器选择foo()impl很有用。超载。

    (一)乙类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T = void>
    class B : public A<B<T>> {

     void foo(...) {
       std::cout <<"B::foo()
    "
    ;  
     }
     void foo( Type2Type< std::enable_if< is_base_of<T,B>::value == true, B>::type> ) {
       static_cast<const T*>(this)->foo( Type2Type<T> );
     }
    }

    这里,如果层次结构的底部由C给出,则不需要typetotype参数。

    (ii)C类

    1
    2
    3
    4
    5
    6
    class C : public B<C> {
     void foo(...) {
       std::cout <<"C::foo()
    "
    ;  
     }
    }

    我知道std::is_base_of returns true if t==b。这里,我们使用我们自己的is_base_of the returns false_type,当两个模板参数相同时。类似的东西

    1
    2
    3
    4
    5
    6
    template<class B, class D>
    struct is_base_of : public is_convertible<D *, B *> {};
    template<class B, class B>
    struct is_base_of<B, B> :  public false_type {};
    template<class D>
    struct is_base_of<void, D> : public false_type {};

    结论:如果std::enable_失败,那么foo()的变量版本将是唯一可用的版本(因为sfinae),编译器将实现foo的B版本。但是,如果启用"if"没有失败,编译器将选择第二个版本(因为当编译器试图找出重载函数之间的最佳匹配时,variadic是最后一个选项)。