You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在未知模板参数时将C++模板类传入构造函数?

问题描述

本人有Java开发背景,许久未使用C++,目前用其进行单片机开发。在主函数中初始化了一个用于电子纸屏绘制的模板化显示对象:

GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(SS, 17, 16, 4));

希望将该对象传入负责绘制操作的Graph类的构造函数,当前Graph类构造函数定义如下:

class Graph {
public:
    Graph(GxEPD2_BW *display);
    // ...
};

但C++报错,提示模板类GxEPD2_BW缺少参数列表。需求是让Graph类不依赖特定的显示类型,能接收任意参数化的显示对象,类似Java中GxEPD2_BW<?, ?>通配符的效果。

解决方案

C++完全支持这种“通用类型适配”的需求,有两种常用实现方式,适配不同场景:

方法1:将Graph定义为模板类

这是C++中最直接的适配方式,无需修改原有库代码,也没有额外性能损耗:

template <typename DisplayType, int Height>
class Graph {
private:
    GxEPD2_BW<DisplayType, Height>* _display;
public:
    // 构造函数接收对应模板参数的GxEPD2_BW指针
    Graph(GxEPD2_BW<DisplayType, Height>* display) : _display(display) {}
    
    // 示例绘制方法,直接调用显示对象的成员函数
    void drawHello() {
        _display->fillScreen(GxEPD_WHITE);
        _display->setCursor(10, 10);
        _display->print("Hello E-Paper");
    }
};

使用方式

主函数中实例化Graph时,C++17及以上版本可以自动推导模板参数:

GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(SS, 17, 16, 4));
Graph graph(&display); // 自动推导模板参数
graph.drawHello();

如果使用C++17之前的标准,需要显式指定模板参数:

Graph<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> graph(&display);

方法2:基于抽象基类的多态实现

如果希望Graph保持非模板类的结构(更接近Java的接口风格),可以通过抽象基类实现:

步骤1:定义抽象显示接口

class DisplayInterface {
public:
    // 必须声明虚析构函数,避免子类对象通过基类指针释放时的内存泄漏
    virtual ~DisplayInterface() = default;
    
    // 声明所有需要用到的绘制纯虚函数(根据实际需求补充)
    virtual void fillScreen(uint16_t color) = 0;
    virtual void setCursor(int16_t x, int16_t y) = 0;
    virtual void print(const char* text) = 0;
};

步骤2:让GxEPD2_BW继承该接口

如果原GxEPD2_BW类未继承此接口,需要修改库代码或封装一层:

template <typename DisplayType, int Height>
class GxEPD2_BW : public DisplayInterface {
    // 原类的成员和实现...
    
    // 重写纯虚函数,调用自身的成员方法
    void fillScreen(uint16_t color) override {
        this->GxEPD2_BW::fillScreen(color);
    }
    
    void setCursor(int16_t x, int16_t y) override {
        this->GxEPD2_BW::setCursor(x, y);
    }
    
    void print(const char* text) override {
        this->GxEPD2_BW::print(text);
    }
};

步骤3:修改Graph类接收基类指针

class Graph {
private:
    DisplayInterface* _display;
public:
    Graph(DisplayInterface* display) : _display(display) {}
    
    void drawHello() {
        _display->fillScreen(GxEPD_WHITE);
        _display->setCursor(10, 10);
        _display->print("Hello E-Paper");
    }
};

使用方式

直接传入任意实现了DisplayInterface的显示对象即可:

GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(SS, 17, 16, 4));
Graph graph(&display);
graph.drawHello();

两种方式对比

  • 模板类方式:无虚函数调用的性能损耗,适合单片机资源有限的场景,无需修改原有库代码,是更推荐的C++原生方案。
  • 抽象基类方式:Graph类结构更简洁(非模板),但存在虚函数的微小性能开销,且需要确保显示类继承了接口(可能需要修改或封装库)。

内容的提问来源于stack exchange,提问作者user3792852

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:05:26