简体   繁体   English

CRTP和多级继承

[英]CRTP and multilevel inheritance

A friend of mine asked me "how to use CRTP to replace polymorphism in a multilevel inheritance" . 我的一个朋友问我“如何使用CRTP替换多级继承中的多态” More precisely, in a situation like this: 更准确地说,在这种情况下:

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 */ }
}

My friend and I understand that CRTP is not a drop-in replacement for polymorphism but we are interested in cases where both patterns can be used. 我和我的朋友都明白,CRTP并不是多态性的直接替代品,但我们对可以使用这两种模式的情况感兴趣。 (For the sake of this question, we are not interested in pros and cons of each pattern.) (为了这个问题,我们对每种模式的利弊都不感兴趣。)

  1. This question has been asked before but it turned out the the author wanted to implement the named parameter idiom and his own answer focus on this problem more than on the CRTP. 之前已经问过这个问题 ,但事实证明,作者想要实现命名参数idiom并且他自己的答案更多地关注这个问题而不是CRTP。 On the other hand, the most voted answer seems to be just about a derived class method calling its homonym in the base class. 另一方面,投票最多的答案似乎只是一个派生类方法在基类中调用它的同音词。

  2. I came up with an answer (posted below) which has quite a lot of boilerplate code and I wonder if there are simpler alternatives. 我想出了一个答案(下面发布),它有很多样板代码,我想知道是否有更简单的替代品。

(1) The topmost class in the hierarchy looks like: (1)层次结构中最顶层的类看起来像:

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<T>::foo() behaves similarly to a pure virtual method in the sense that it doesn't have a " default implementation " and calls are directed to derived classes. A<T>::foo()行为类似于纯虚方法,因为它没有“ 默认实现 ”,并且调用被定向到派生类。 However, this doesn't prevent A<T> from being instantiated as a non base class. 但是,这不会阻止A<T>被实例化为非基类。 To get this behavior A<T>::~A() is made protected . 为了获得这种行为, A<T>::~A() protected

Remark: Unfortunately a GCC bug turns special member functions public when = default; 备注:不幸的是,当= default;时, GCC错误会将特殊成员函数公开= default; is used. 用来。 In this case, one should used 在这种情况下,应该使用一个

protected:
    ~A() {}

Still, protecting the destructor is not enough for the cases where a call to a constructor is not matched by a call to the destructor (this might happen via operator new ). 尽管如此,保护析构函数还不足以满足对构造函数的调用与析构函数调用不匹配的情况(这可能通过operator new发生)。 Hence, it's advisable to protect all constructors (including copy- and move-constructor) as well. 因此,建议保护所有构造函数(包括复制和移动构造函数)。

When instantiations of A<T> should be allowed and A<T>::foo() should behave like a non-pure virtual method, then A should be similar to the template class B below. 当的实例A<T>应允许和A<T>::foo()应表现得象一个非纯虚方法,那么A应该类似于模板类B下方。

(2) Classes in the middle of the hierarchy (or the topmost one, as explained in the paragraph above) look like: (2)层次结构中间的类(或最上面的类,如上段所述)看起来像:

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()\n";
  }

  // 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();
  }

};

Constructors and destructors are public and T defaults to void . 构造函数和析构函数是公共的, T默认为void This allows objects of type B<> to be the most derived in the hierarchy and makes this legal: 这允许类型为B<>对象在层次结构中派生得最多,并使其合法:

B<> b;
b.foo();

Notice also that B<T>::foo() behaves as a non pure virtual method in the sense that, if B<T> is the most derived class (or, more precisely, if T is void ), then b.foo(); 另请注意, B<T>::foo()在某种意义上表现为非纯虚方法,如果B<T>是派生程度最高的类(或者更确切地说,如果Tvoid ),那么b.foo(); calls the " default implementation of foo() " (which outputs B::foo() ). 调用“ foo() 默认实现 ”(输出B::foo() )。 If T is not void , then the call is directed to the derived class. 如果T不为void ,则调用将定向到派生类。 This is accomplished through tag dispatching. 这是通过标签调度来完成的。

The test &B::foo == &T::foo is required to avoid an infinite recursive call. 测试&B::foo == &T::foo是避免无限递归调用所必需的。 Indeed, if the derived class, T , doesn't reimplement foo() , the call static_cast<const T*>(this)->foo(); 的确,如果派生类T不重新实现foo() ,则调用static_cast<const T*>(this)->foo(); will resolve to B::foo() which calls B::foo_impl(std::false_type) again. 将解析为B::foo() B::foo_impl(std::false_type)再次调用B::foo_impl(std::false_type) Furthermore, this test can be resolved at compile time and the code is either if (true) or if (false) and the optimizer can remove the test altogether (eg GCC with -O3). 此外,此测试可以在编译时解决,代码可以是if (true)if (false) ,优化器可以完全删除测试(例如GCC with -O3)。

(3) Finally, the bottom of the hierarchy looks like: (3)最后,层次结构的底部看起来像:

class C : public B<C> {

public:

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

};

Alternatively, one can remove C::foo() entirely if the inherited implementation ( B<C>::foo() ) is adequate. 或者,如果继承的实现( B<C>::foo() )足够,可以完全删除C::foo()

Notice that C::foo() is similar to a final method in the sense that calling it does not redirected the call to a derived class (if any). 请注意, C::foo()类似于最终方法,因为调用它不会将调用重定向到派生类(如果有的话)。 (To make it non final, a template class like B should be used.) (为了使它不是最终的,应该使用像B这样的模板类。)

(4) See also: (4)另见:

How to avoid errors while using CRTP? 使用CRTP时如何避免错误?

Note: This isn't specifically a solution to the "final override" problem, but to the CRTP multi-level inheritance problem in general (since I haven't found an answer anywhere on how to do it, and I think my findings would be useful). 注意:这不是特定的“最终覆盖”问题的解决方案,而是一般的CRTP多级继承问题(因为我没有在任何地方找到答案如何做到这一点,我认为我的发现会很有用)。

EDIT : I've posted a solution to the final override problem here 编辑 :我已经在这里发布了最终覆盖问题的解决方案

I recently learned of CRTP and its potential as a static replacement for runtime polymorphism. 我最近了解到CRTP及其作为运行时多态性的静态替代的潜力。 After searching for a while to see whether CRTP could be used as a like-for-like "drop-in" replacement for polymorphism, such that you could use multi-level inheritance and the like, I have to say, I was pretty surprised that I couldn't find a proper generic solution anywhere without boilerplate which could scale indefinitely. 在搜索了一段时间之后,看看CRTP是否可以用作类似的“插入式”替换多态,这样你可以使用多级继承等,我不得不说,我很惊讶我没有找到一个适当的通用解决方案,没有可以无限扩展的样板。 After all, why not try to make CRTP a drop-in replacement for polymorphism, given all of its performance benefits? 毕竟,为什么不尝试使CRTP成为多态性的替代品,因为它具有所有性能优势? Some investigation ensued, and here's what I came up with: 随后进行了一些调查,这就是我想出的:

The problem: 问题:

The classic CRTP pattern creates a "loop" of accessibility between the CRTP interface and the implementation class. 经典的CRTP模式在CRTP接口和实现类之间创建了一个可访问性的“循环”。 (The CRTP interface class has access to the "base" implementation class via a static cast of itself to the template parameter type, and the implementation class inherits the public interface from the CRTP interface class.) When you create a concrete implementation, you are closing the loop, making it very difficult to inherit from the concrete implementation class, such that whatever derives from it also behaves in a polymorphic manner. (CRTP接口类可以通过静态转换为模板参数类型访问“基础”实现类,实现类从CRTP接口类继承公共接口。)当您创建具体实现时,您是关闭循环,使得从具体实现类继承非常困难,因此从它派生的任何东西也都以多态方式运行。

Classic CRTP single-level inheritance 经典的CRTP单级继承

The solution: 解决方案:

Separate the pattern into three concepts: 将模式分为三个概念:

  • The "abstract interface class", ie the CRTP interface. “抽象接口类”,即CRTP接口。
  • The "inheritable implementation class", which can be indefinitely inherited from by other inheritable implementation classes. “可继承的实现类”,可以无限期地继承自其他可继承的实现类。
  • The "concrete class", which combines the abstract interface with the desired inheritable implementation class, and closes the loop. “具体类”,它将抽象接口与所需的可继承实现类相结合,并关闭循环。

Here's a diagram to help illustrate: 这是一个有助于说明的图表:

Multiple-level inheritance with CRTP 使用CRTP进行多级继承

The trick is to pass the concrete implementation class as the template parameter all the way down through all of the inheritable implementation classes into the abstract interface class. 诀窍是将具体实现类作为模板参数传递到所有可继承的实现类,直到抽象接口类。

With this approach you can: 通过这种方法,您可以:

  1. inherit implementations indefinitely, 无限期地继承实现,
  2. call the highest implemented method in a CRTP multi-level inheritance chain from any level, 在任何级别的CRTP多级继承链中调用最高实现的方法,
  3. design each implementation in an hierarchy agnostic manner, 以层次结构不可知的方式设计每个实现,
  4. forget having to use boilerplate code (well, no more than with classic single-level CRTP anyway), 忘了不得不使用样板代码(好吧,不过只有经典的单级CRTP),

which reflects virtual/runtime polymorphism perfectly. 它完美地反映了虚拟/运行时多态性。

Example code: 示例代码:

#include <iostream>

template <class Top>
struct CrtpInterface
{
  void foo()
  {
    std::cout << "Calling CrtpInterface::foo()\n";
    fooImpl();
  }
  void foo2()
  {
    std::cout << "Calling CrtpInterface::foo2()\n";
    fooImpl2();
  }
  void foo3()
  {
    std::cout << "Calling CrtpInterface::foo3()\n";
    fooImpl3();
  }
  void foo4()
  {
    std::cout << "Calling CrtpInterface::foo4()\n";
    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()\n";
  }

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

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

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

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

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

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

struct B : BImpl<B>
{
};

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

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()\n";
  }
};

struct D : DImpl<D>
{
};

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

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

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

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

Which prints: 哪个印刷品:

### 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()

Using this approach, and a "variant-style" wrapper (built using some sechsy variadic templates and macros, maybe I'll post that later), which acted like a pointer to a virtual abstract base class, I was able to effectively create a vector of CRTP classes inheriting from the same interface. 使用这种方法,以及“变体样式”包装器(使用一些sechsy可变参数模板和宏构建,也许我稍后会发布),它就像一个指向虚拟抽象基类的指针,我能够有效地创建一个从同一接口继承的CRTP类的向量。

I measured the performance in comparison to a vector of like-for-like virtual classes, all based on an equivalent virtual interface, and I found that with this approach, depending on the scenario, I could achieve a performance increase of up to 8x! 我测量了性能与类似的类虚拟类的向量相比较,所有这些都基于等效的虚拟接口,我发现使用这种方法,根据场景,我可以实现高达8倍的性能提升! This is very encouraging, given the relatively little overhead needed to generate a functionally polymorphic CRTP class hierarchy! 考虑到生成功能多态CRTP类层次结构所需的开销相对较少,这非常令人鼓舞!

After realising that my original answer didn't actually deal with the final override question at hand, I thought I'd add to it. 在意识到我的原始答案实际上没有处理手头的最终覆盖问题后,我想我会加入它。 I wanted to come up with a "final override" solution in a similar manner as my previous answer. 我想以与我之前的答案类似的方式提出“最终覆盖”解决方案。

The problem: 问题:

The CRTP interface class always redirects via a static cast to the highest derived class. CRTP接口类始终通过静态强制转换重定向到最高派生类。 This is at odds with the concept of a "final" function: if the desired "final" function is not implemented on the highest derived class, and is "overridden" by a higher class (since you cannot give a function a "final" property unless it is virtual, which we are trying to avoid in CRTP), the CRTP interface will redirect not to the desired "final" function but to the "override". 这与“最终”函数的概念不一致:如果所需的“最终”函数没有在最高派生类上实现,并且被更高级别“覆盖”(因为你不能给函数一个“最终”)属性,除非它是虚拟的,我们试图在CRTP中避免),CRTP接口将不会重定向到所需的“最终”功能,而是重定向到“覆盖”。

The solution: 解决方案:

Split the interface into three concepts: 将界面拆分为三个概念:

  • The abstract interface class without any of the redirecting functions, which inherits: 没有任何重定向函数的抽象接口类,它继承:
  • an abstract redirecting class whose redirecting functions redirect to the highest derived class, unless one or more of the redirecting functions is overridden by: 一个抽象重定向类,其重定向函数重定向到最高派生类, 除非一个或多个重定向函数被重写:
  • a concrete "redirect override" class, which overrides the redirecting functions with an implementation. 一个具体的“重定向覆盖”类,它使用实现覆盖重定向功能。

When instantiating the concrete implementation class, instead of passing the concrete implementation class as a template parameter through all of the "inheritable implementation classes" into the interface, we pass the redirecting class which the interface will inherit from as the template parameter. 在实例化具体实现类时,不是通过所有“可继承实现类”将具体实现类作为模板参数传递到接口中,而是将接口将继承的重定向类作为模板参数传递。

When we want to make a function "final", we simply create a "redirect override class" which inherits from the abstract redirecting class, and overrides the redirecting function which we want to make final. 当我们想要创建一个函数“final”时,我们只需创建一个“重定向覆盖类”,它继承自抽象重定向类,并覆盖我们想要最终的重定向函数。 Then we pass this new "redirect override class" as a parameter through all of the inheritable implementation classes. 然后我们通过所有可继承的实现类将这个新的“重定向覆盖类”作为参数传递。

With this approach: 用这种方法:

  1. "final" functions are called directly rather than redirected via a cast (unless you need the "final" function to be implemented in the inheritable implementation class rather than the redirect override class), “final”函数是直接调用的,而不是通过强制转换重定向(除非你需要在可继承的实现类而不是重定向覆盖类中实现“final”函数),
  2. "final" functions cannot be overridden by any future user code, 任何未来的用户代码都不能覆盖“final”函数,
  3. each "final" function only requires an extra ImplFinal class per level of inheritance, with no extra boilerplate. 每个“final”函数只需要每个继承级别一个额外的ImplFinal类,没有额外的样板。

This all sounds very complicated, so here is a flow diagram I made in attempt to make things easier to understand: 这一切听起来都非常复杂,所以这里是我试图让事情更容易理解的流程图:

DImpl and EImpl have final functions which cannot be overridden when either DImpl or EImpl are inherited from: DImpl和EImpl具有最终功能,当DImpl或EImpl继承自:时,不能覆盖这些功能:

Example code: 示例代码:

#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()\n";
    fooImpl();
  }

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

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

  void foo4()
  {
    std::cout << "Calling Interface::foo4()\n";
    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()\n";
  }

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

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

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

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

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

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

struct B : BImpl<B>
{
};

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

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()\n";
  }
};


// 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()\n";
  }

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

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

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

  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()\n";
    // This will only call DFinal::fooImpl4();
    std::cout << "Calling fooImpl4() polymorphically. (Should not print FImplFinal::fooImpl4() or EImpl::fooImpl4())\n";
    impl::fooImpl4();
  }

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

struct F : FImpl<F>
{
};

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

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

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

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

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

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

The code prints: 代码打印:

### 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()
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
};

if foo() in C is absent the one in B will be invoked. 如果C中的foo()不存在,则将调用B中的一个。 Otherwise the one in B will be overridden. 否则B中的那个将被覆盖。

Multilevel inheritance is not the problem, but how CRTP creates polymorphism is. 多级继承不是问题,但CRTP如何创建多态性。

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();
}

One can also introduce a derived method to avoid wordy type casting at each invocation. 还可以引入derived方法以避免在每次调用时出现冗长类型转换。

template<typename Derived>
struct Base
{
    Derived& derived() { return *static_cast<Derived*>(this); }
    const Derived& derived() const { return *static_cast<const Derived*>(this); }
};

Here is a possible implementation that can reduce the boilerplate code inside the class (but not the total amount of auxiliary code). 这是一个可能的实现,可以减少类中的样板代码(但不是辅助代码的总量)。

The idea of this solution is to use SFINAE and overloading to select the impl function. 该解决方案的想法是使用SFINAE和重载来选择impl函数。

(i) Class A (i)A类

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

where TypetoType is the template struct 其中TypetoType是模板结构

template< typename T > struct Type2Type {
  typedef T OriginalType
}

which is useful for helping compiler to choose the foo() impl. 这对于帮助编译器选择foo()impl非常有用。 by overloading. 通过重载。

(i) Class B (i)B类

template <typename T = void>
class B : public A<B<T>> { 

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

Here, the TypetoType argument is not necessary if the bottom of the hierarchy is given by C. 这里,如果层次结构的底部由C给出,则不需要TypetoType参数。

(ii) Class C (ii)C类

class C : public B<C> { 
 void foo(...) { 
   std::cout << "C::foo()\n";  
 }
}

I know that std::is_base_of returns true if T==B. 我知道如果T == B,std :: is_base_of会返回true。 Here, we use our own version of is_base_of that returns false_type when the two template arguments are the same. 在这里,我们使用我们自己的is_base_of版本,当两个模板参数相同时返回false_type。 Something like 就像是

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 {};

Conclusion: if std::enable_if fails, then the variadic version of foo() will be the only one available (because of SFINAE) and compiler will implement B version of foo. 结论:如果std :: enable_if失败,则foo()的可变版本将是唯一可用的版本(因为SFINAE),编译器将实现foo的B版本。 However, if enable_if does not fail, compiler will choose the second version (because variadic is the last option when compiler try to figure out the best match between overloading functions). 但是,如果enable_if没有失败,编译器将选择第二个版本(因为当编译器试图找出重载函数之间的最佳匹配时,variadic是最后一个选项)。

There is a lot of stuff going on in this thread which I do not find useful, so I share here my own solution to this problem. 这个帖子中有很多东西我觉得没用,所以我在这里分享我自己的解决这个问题的方法。

CRTP is mainly a pattern for code reduction. CRTP主要是用于代码减少的模式。 In order to work properly, it is necessary that at each level of the inheritance hierachy, one is able to call all functions from the levels below -- just as in usual dynamic inheritance. 为了正常工作,有必要在继承层次结构的每个级别上,能够从下面的级别调用所有函数 - 就像通常的动态继承一样。

However, in CRTP, each stage must further be aware of the final type, which currently derives from it, because in the end this is the whole point of CRTP -- to call functions that always apply to the current (static) final type. 但是,在CRTP中,每个阶段必须进一步了解当前从中派生的最终类型,因为最终这是CRTP的全部 - 调用始终应用于当前(静态)最终类型的函数。

One can obtain this by adding a layer of indirection at each level of the static inheritance hierarchy, as in the following example: 可以通过在静态继承层次结构的每个级别添加一个间接层来获得此结果,如下例所示:

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 ...

One can use it with a final type as in the following: 可以使用最终类型,如下所示:

#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
}

Alternatively, one can use each level for its own by just dropping the _impl suffix: 或者,只需删除_impl后缀,就可以使用每个级别:

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

This is a nice thing, which notably does not work with the other approaches in this thread such as 这是一件好事,特别是对于这个线程中的其他方法,例如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM