C++使用模板出现链接错误,如何保留Initialize函数在cpp文件中
问题根因
定义在头文件的模板函数GfxDevice::BindConstantBuffer<T>会在所有调用它的编译单元中分别实例化,实例化过程需要依赖GfxBufferResource::Initialize()的符号定义,如果符号可见性不足就会触发链接错误。以下方案都可以在保留Initialize()放在cpp文件的前提下解决问题:
可行方案
方案1:修正Initialize()的链接可见性
- 检查
GfxBufferResource类定义,确认Initialize()没有被错误标记为static、内部可见属性,也不是未实现的纯虚函数 - 确认存放
Initialize()实现的cpp文件已被加入项目编译列表,没有被遗漏 - 如果
GfxBufferResource属于动态库导出类,确保Initialize()被正确标记了导出符号(Windows下为__declspec(dllexport/import),Linux下为__attribute__((visibility("default"))))
方案2:显式实例化模板函数
在GfxDevice类实现的cpp文件(和Initialize()同编译单元)末尾,显式实例化所有项目中会用到的模板参数版本:
// 替换YourType1、YourType2为实际用到的模板参数类型 template void GfxDevice::BindConstantBuffer<YourType1>(unsigned int stage, GfxConstantBuffer<YourType1>* constantBuffer, unsigned int binding); template void GfxDevice::BindConstantBuffer<YourType2>(unsigned int stage, GfxConstantBuffer<YourType2>* constantBuffer, unsigned int binding);
该方案让模板实例化和Initialize()在同一个编译单元生成,直接解决符号找不到的问题。
方案3:增加非模板中间层封装
如果无法枚举所有用到的模板参数,可以新增非模板的封装函数绕开模板实例化的符号依赖:
- 在
GfxBufferResource头文件新增非模板成员函数声明:
void InitIfNeeded();
- 在存放
Initialize()的cpp文件中实现该函数:
void GfxBufferResource::InitIfNeeded() { if (!Initialized()) { Initialize(); } }
- 修改头文件中的模板函数,替换原来的判断和
Initialize()调用逻辑:
template<typename T> void GfxDevice::BindConstantBuffer(unsigned int stage, GfxConstantBuffer<T>* constantBuffer, unsigned int binding) { GfxBufferResource* bufferResource = constantBuffer->GetBufferResource(); bufferResource->InitIfNeeded(); // 替换原有逻辑 BindConstantBuffer(stage, bufferResource->GetBuffer(), binding); }
该方案把对Initialize()的调用转移到非模板编译单元,不需要修改Initialize()的原有实现和存放位置,适配性最高。
内容的提问来源于stack exchange,提问作者Angry Tomato
相关产品推荐
相关产品推荐

