如何在未知模板参数时将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
相关产品推荐
相关产品推荐

