静态接口-CRTP?混蛋其他?

时间:2019-06-19 05:42:10

标签: c++ interface c++17 mixins crtp

我对接口实现感到困惑。我知道这样做的标准方法是编写一个具有纯虚方法的类,该类将用作接口,例如:

class Interface {
public:
  virtual void doSometing() = 0;
}

并使用它:

class Implementation : public Interface {
public:
  virtual void doSomething() override { ... }
}

或者甚至更好地应用NVI(非虚拟接口)。

但是所有这些函数都使用了我要避免的虚函数-因为我正在为嵌入式程序编写代码,因此必须考虑由于vtable间接性而导致的性能损失。

因此,我专注于“静态多态性”并尝试使用CRTP来实现接口:

template<typename T>
class Interface {
public:
  void doSomething() {
    static_cast<T&>(*this)._doSomething();
}

class Implementation : public Interface<Implementation> {
public:
  void _doSomething() { /* Do implementation specific stuff here */ }
}

class Implmentation2 : public Interface<Implementation2> {...}

到目前为止,一切都很好。但是我看到了一个大麻烦。一旦我想存储一堆指向某个容器的接口的指针并想访问各种实现类的实例,我就会遇到麻烦,Interface<T>本身就是一个不同的类,而不是一个接口:

Interface<Implementation>Interface<Implementation2>是不同的类型。

好吧,让我们创建一个通用的基类来从...派生接口。

template<typename T>
class Interface : public IfaceBase {
public:
  void doSomething() {
    static_cast<T&>(*this)._doSomething();
}

...但是后来我无法使用该接口访问最终实例。在我看来,我正在尝试创建一个界面,无论如何听起来听起来很疯狂……

因此,我发现在这种情况下CRTP不太可用。我搜索了互联网,发现MIXIN似乎是“ CRTP颠倒了”。但是我不确定它是否可以用于我的目的...

能帮我吗?如果有一种方法可以应用MIXINS或任何其他习语/无论如何具有不带虚函数的C ++接口,请分享一下想法:)

在此先感谢任何愿意提供帮助的人!欢呼马丁

1 个答案:

答案 0 :(得分:0)

我知道两种以多态方式从不同类型调用非虚拟方法的方法:

  1. 类型擦除:将非虚拟类包装到具有虚拟方法的其他类中(这与您尝试的相对接近)。
  2. 访客模式应用于variant类型。这要求在调用该方法的编译时知道所有类型。

由于您根本不需要虚拟方法,因此我只介绍解决方案2。如果您对类型擦除更感兴趣,那么C++ 'Type Erasure' Explained (Dave Kilian's blog)是不错的阅读方法。

访问者模式和变体类型(C ++ 17)

假设您的类型具有非虚方法:

class Implementation1 {
    void doSomething() { /* ... */ }
};

class Implementation2 {
    void doSomething() { /* ... */ }
};

您可以使用std::variant将它们的任何组合存储在容器中:

// #include <variant>
// #include <vector>
using ImplVariant = std::variant<Implementation1,Implementation2>;
std::vector<ImplVariant> array = {Implementation2(), Implementation1() /*, ...*/};

要在doSomething对象上调用MyVariant,请向其应用访问者。在C ++ 17中,只需将generic lambda传递到std::visit即可完成:

for (auto& variant : array) {
    std::visit([](auto&& object) { object.doSomething(); }, variant);
}

Live demo

或者,您可以制作variant((智能)指针)。