是否可以在不使用动态调度的情况下创建接口?

Is it possible to create an interface without using dynamic dispatch?

背景

我正在用几个 'choices' 为嵌入式项目编写固件。例如,设备使用湿度传感器,但它支持多种不同的湿度传感器。

为了很好地实现这一点,我编写了一个(抽象的)基础 class 作为表示传感器的接口:

class HumiditySensor {
public:
    virtual void readSample() = 0;
    virtual double getHumidity() const = 0;
    virtual double getTemperature() const = 0;
};

现在,不同的特定传感器可以继承这个基础class并实现纯虚拟方法。

class SHTHumiditySensor : public HumiditySensor {
private:
    SHTSensor &sht;
public:
    explicit SHTHumiditySensor(SHTSensor *sht);
    void readSample() override;
    double getHumidity() const override;
    double getTemperature() const override;
};

class DHTHumiditySensor : public HumiditySensor {
private:
    DHT &dht;
public:
    explicit DHTHumiditySensor(DHT *dht);
    void readSample() override;
    double getHumidity() const override;
    double getTemperature() const override;
};

在我代码的其他部分,我可以只'ask for'一个(指向)HumiditySensor的(指针)而不关心它是什么类型的传感器,或者它是如何实现的,因为它们公开相同的公共接口。例如。在需要湿度传感器的 class 中:

class Humidistat {
private:
    HumiditySensor &hs;
public:
    Humidistat(HumiditySensor *hs);
}

基于配置常量,在 main.cpp 中,我将实例化两个传感器中的任何一个,并在 Humidistat.

的实例化中传递指向它的指针

我的项目中还有几个这样的案例(不同显示器的多个可能的 UI 等)。

Problem/question

这一切都很好,但据我所知,这一切都在我的微控制器上占用了相当多的宝贵时间 flash/RAM,因为它使用了动态调度/运行-time 多态性。

但是,我要使用的两个派生 classes 中的哪一个原则上是固定的并且在编译时已知。因此,最好避免 运行 时间多态性的开销,但这可能吗?

因此,如果我理解正确的话,我想要的是静态(或编译时)多态性。我发现这可以通过 Curiously Recurring Template Pattern (CRTP) 来完成,但它看起来相当 convoluted/hacky 我想知道这是否真的是实现我想要的最简单的方法。

如果你想避免运行时多态性的代价,那么你必须依赖编译时多态性。编译时多态的工具是模板。

是的,模板可能更难使用;编译时间限制了可以做什么。您提到的 CRTP 在某些用例中是一种有用的技术,但我认为在所示示例中不需要它。

在最简单的情况下,您可以使用如下模板:

template <class Sensor>
class HumiditySensor {
   Sensor &sht;
   // ...
};

// If needed:
// using SHTHumiditySensor = HumiditySensor<SHTSensor>;
// using DHTHumiditySensor = HumiditySensor<DHT>;

template <class Sensor>
class Humidistat {
    HumiditySensor<Sensor> &hs;
    // ...
}

要指定如何定义可在此类模板中使用的 Sensor class,您只需指定具有 class 的 概念 某些成员。这主要是 STL 的定义方式。以前,此类概念已写入文档,但在 C++20 中,现在有一种以编程方式定义它们的方法——这在很大程度上是可选的,但可以带来一些好处。

你只有一个class,所以不存在定义上的多态性。您不需要继承、接口、CRTP 或任何此类技巧。

class SHTHumiditySensor /* no ': public HumiditySensor' needed */ {
private:
    SHTSensor &sht;
public:
    explicit SHTHumiditySensor(SHTSensor *sht);
    void readSample() /* no virtual, no override */;
    double getHumidity() const /* no virtual, no override */;
    double getTemperature() const /* no virtual, no override */;
};

using HumiditySensor = SHTHumiditySensor;