c++ - CRTP 中未实现的派生函数

标签 c++ oop c++11 abstract crtp

我正在努力制作一个包装器,以便能够将 future 的代码轻松移植到不同的后端渲染引擎。我们目前在 GDI 工作。目前我正在抽象后端上实现虚拟函数,但我想将其更改为 CRTP,因为后端应该在编译时已知。

不幸的是,我在使用 CRTP(第一次使用)时遇到的一个问题是我必须实现派生函数的所有细节。相反,抽象实现不需要完全实现的派生子项。为了演示,请考虑以下内容:

#include <Windows.h>
#include <iostream>

struct AbstractBackend
{
  virtual ~AbstractBackend() = 0;

  virtual void foo()
  {
    throw "implementation missing: failed to override in derived class";
  }

  virtual void bar()
  {
    throw "implementation missing: failed to override in derived class";
  }
};

AbstractBackend::~AbstractBackend() {}

struct ConcreteBackendA : AbstractBackend
{
  int backendResource;

  ConcreteBackendA(int rsc) :
    backendResource(rsc)
  {}

  virtual void foo()
  {
    printf("executing ConcreteBackendA::foo!\n");
  }

  // ConcreteBackendA does not support "bar" feature
};

struct ConcreteBackendB : AbstractBackend
{
  HDC backendResource;

  ConcreteBackendB(HDC hdc) :
    backendResource(hdc)
  {}

  virtual void foo()
  {
    printf("executing ConcreteBackendB::foo!\n");
  }

  virtual void bar()
  {
    printf("executing ConcreteBackendB::bar!\n");
  }

};

struct FrontEnd
{
  AbstractBackend *backend;

  FrontEnd(int rsc) :
    backend(new ConcreteBackendA(rsc))
  {}

  FrontEnd(HDC hdc) :
    backend(new ConcreteBackendB(hdc))
  {}

  ~FrontEnd()
  {
    delete backend;
  }

  void foo()
  {
    backend->foo();
  }

  void bar()
  {
    backend->bar();
  }
};

int main()
{
  int rsc = 0;
  HDC hdc = 0;
  FrontEnd A(rsc);
  FrontEnd B(hdc);

  A.foo();
  A.bar(); // throws an error, A::bar is not a feature of this engine

  B.foo();
  B.bar();

  std::cin.get();
}

在此示例中,AbstractBackend 支持两个功能:foo 和 bar。 ConcreteBackendA 仅支持 foo,bar 是它无法支持的函数(可能类似于 Draw3dText),但没关系。用户可以捕获异常并继续。一个小缺点是虚拟函数的使用。我想考虑像这样使用 CRTP:

#include <Windows.h>
#include <iostream>

template <class Derived>
struct AbstractBackend
{
  virtual ~AbstractBackend() = 0;

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

  void bar()
  {
    static_cast<Derived*>(this)->bar();
  }
};

template <class Derived>
AbstractBackend<Derived>::~AbstractBackend() {}

struct ConcreteBackendA : AbstractBackend<ConcreteBackendA>
{
  int backendResource;

  ConcreteBackendA(int rsc) :
    backendResource(rsc)
  {}

  void foo()
  {
    printf("executing ConcreteBackendA::foo!\n");
  }

  // ConcreteBackendA does not support "bar" feature
};

struct ConcreteBackendB : AbstractBackend<ConcreteBackendB>
{
  HDC backendResource;

  ConcreteBackendB(HDC hdc) :
    backendResource(hdc)
  {}

  void foo()
  {
    printf("executing ConcreteBackendB::foo!\n");
  }

  void bar()
  {
    printf("executing ConcreteBackendB::bar!\n");
  }
};

template <class ConcreteBackend>
struct FrontEnd
{
  AbstractBackend<ConcreteBackend> *backend;

  FrontEnd(int rsc) :
    backend(new ConcreteBackendA(rsc))
  {}

  FrontEnd(HDC hdc) :
    backend(new ConcreteBackendB(hdc))
  {}

  ~FrontEnd()
  {
    delete backend;
  }

  void foo()
  {
    backend->foo();
  }

  void bar()
  {
    backend->bar();
  }
};

int main()
{
  int rsc = 0;
  HDC hdc = 0;
  FrontEnd<ConcreteBackendA> A(rsc);
  FrontEnd<ConcreteBackendB> B(hdc);

  A.foo();
  A.bar(); // no implementation: stack overflow

  B.foo();
  B.bar();

  std::cin.get();
}

问题在于,如果派生类无法实现 AbstractBackend 中的函数,则 AbstractBackend 将调用自身,从而导致堆栈溢出。

如何使用 CRTP 复制虚拟抽象实现的行为?

最佳答案

template <class Derived>
struct AbstractBackend
{
  virtual ~AbstractBackend() = 0;

  void foo()
  {
    static_cast<Derived*>(this)->foo_impl();
  }

  void bar()
  {
    static_cast<Derived*>(this)->bar_impl();
  }

  void foo_impl()
  {
    throw "implementation missing: failed to override in derived class";
  }

  void bar_impl()
  {
    throw "implementation missing: failed to override in derived class";
  }

};

现在您可以拥有 foo 的默认实现/bar .

派生类重写 foo_impl而不是foo .

但是,这种特殊用途是一个糟糕的计划;你在编译时知道如果给定 AbstractBackend<D>是否已实现。

毕竟,我们正在实现编译时“动态”dipatch;为什么不在编译时评估错误?

  void foo_impl() = delete;
  void bar_impl() = delete;

现在,当代码在编译时完成分派(dispatch)时,您会收到错误消息,而不是等到编译时。

关于c++ - CRTP 中未实现的派生函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54484464/

相关文章:

c++ - 要抛出的对象的构造函数可以抛出异常吗?

c++ - 为什么 boost::diagnostic_information 崩溃了以及如何修复它?

javascript - 如何在 Typescript 中扩展组件,并找出所需的参数?用例 : Angular - extending mat-side-nav

c++ - 检查数组中消息的最有效方法

c++ - 如何确保 initializer_list 不为零

C++/Cocos2d-x : how to add AdMob in my Cocos2d-x multiplatform project (iOS, 安卓...)?

c++ - 制作模板类后不是类、命名空间、枚举?

c++ - 使用 `cout` 在控制台查看二进制数据

c++ - 类包含其他类问题的字段

类的 Java OOP 实例