静态接口 - CRTP?混合?其他?
Static interface - CRTP? Mixin? Other?
我对接口实现很感兴趣。我知道这样做的标准方法是编写一个 class 具有将用作接口的纯虚拟方法的代码,例如:
class Interface {
public:
virtual void doSometing() = 0;
}
并使用它:
class Implementation : public Interface {
public:
virtual void doSomething() override { ... }
}
或者应用 NVI(非虚拟接口)更好。
但是所有这些都使用我试图避免的虚函数 - 因为我正在为嵌入式编码,其中必须考虑由于 vtable 间接寻址导致的性能损失。
因此我专注于 'static polymorphism' 并尝试使用 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> {...}
到目前为止一切顺利。但我看到了一个大麻烦。一旦我想存储一堆指向某个容器接口的指针并想访问各种实现的实例 classes 我遇到了麻烦 Interface<T>
总是不同的 class本身,而不是单一接口:
Interface<Implementation>
和Interface<Implementation2>
是不同的类型。
好的,让我们创建一个公共基础 class 以从...派生接口...
template<typename T>
class Interface : public IfaceBase {
public:
void doSomething() {
static_cast<T&>(*this)._doSomething();
}
...但是我无法使用该接口访问最终实例。在我看来,我正在尝试创建一个接口,这个接口本身听起来很疯狂......
所以我发现 CRTP 在这种情况下不太有用。我在网上搜索了一下,发现 MIXIN 好像是 "CRTP turned upside down"。但是我不确定它是否可以用于我的目的...
你能帮帮我吗?如果有办法如何应用 MIXINS 或任何其他 idiom/whatever 来获得没有虚拟的 C++ 接口,请分享这个想法:)
非常感谢任何愿意提供帮助的人!干杯马丁
我知道以多态方式从不同类型调用 non-virtual 方法的两种方法:
- 类型擦除:将 non-virtual 类 包装到其他具有虚拟方法的方法中(这与您尝试的比较接近)。
- 应用于
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() /*, ...*/};
要在 MyVariant
对象上调用 doSomething
,您需要为其应用一个访问者。这在 C++17 中只需传递 generic lambda to std::visit:
for (auto& variant : array) {
std::visit([](auto&& object) { object.doSomething(); }, variant);
}
或者,您可以制作 variant
个(智能)指针。
我对接口实现很感兴趣。我知道这样做的标准方法是编写一个 class 具有将用作接口的纯虚拟方法的代码,例如:
class Interface {
public:
virtual void doSometing() = 0;
}
并使用它:
class Implementation : public Interface {
public:
virtual void doSomething() override { ... }
}
或者应用 NVI(非虚拟接口)更好。
但是所有这些都使用我试图避免的虚函数 - 因为我正在为嵌入式编码,其中必须考虑由于 vtable 间接寻址导致的性能损失。
因此我专注于 'static polymorphism' 并尝试使用 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> {...}
到目前为止一切顺利。但我看到了一个大麻烦。一旦我想存储一堆指向某个容器接口的指针并想访问各种实现的实例 classes 我遇到了麻烦 Interface<T>
总是不同的 class本身,而不是单一接口:
Interface<Implementation>
和Interface<Implementation2>
是不同的类型。
好的,让我们创建一个公共基础 class 以从...派生接口...
template<typename T>
class Interface : public IfaceBase {
public:
void doSomething() {
static_cast<T&>(*this)._doSomething();
}
...但是我无法使用该接口访问最终实例。在我看来,我正在尝试创建一个接口,这个接口本身听起来很疯狂......
所以我发现 CRTP 在这种情况下不太有用。我在网上搜索了一下,发现 MIXIN 好像是 "CRTP turned upside down"。但是我不确定它是否可以用于我的目的...
你能帮帮我吗?如果有办法如何应用 MIXINS 或任何其他 idiom/whatever 来获得没有虚拟的 C++ 接口,请分享这个想法:)
非常感谢任何愿意提供帮助的人!干杯马丁
我知道以多态方式从不同类型调用 non-virtual 方法的两种方法:
- 类型擦除:将 non-virtual 类 包装到其他具有虚拟方法的方法中(这与您尝试的比较接近)。
- 应用于
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() /*, ...*/};
要在 MyVariant
对象上调用 doSomething
,您需要为其应用一个访问者。这在 C++17 中只需传递 generic lambda to std::visit:
for (auto& variant : array) {
std::visit([](auto&& object) { object.doSomething(); }, variant);
}
或者,您可以制作 variant
个(智能)指针。