MSVC编译Qt项目跨DLL使用QMap出现LNK2005与LNK1169错误如何解决
MSVC 2015编译Qt 5.12.6项目LNK2005/LNK1169报错解决方案
报错根因
- 该问题由MSVC与GCC/Clang的C++模板实例化规则差异导致:对于隐式实例化的模板类成员函数,MSVC默认会在所有用到该模板实例的编译单元中生成独立的导出符号,不会自动合并重复项;而GCC/Clang会将这类符号标记为弱符号,链接阶段自动去重,所以使用后两款编译器不会报错。
- 你的三个依赖模块(AAA、Core、Common)均使用了
QMap<int,double>实例,Core.dll编译时已经将该类的析构函数符号导出到Core.lib中,AAA库编译时xxx.obj生成的同名称符号就会和Core.lib中的符号冲突,触发LNK2005报错。 - 改为指针返回值后不报错的原因是:使用指针类型不需要完整的模板类定义,不会触发模板实例化,自然不会生成重复符号。
可行解决方案
以下方案按改造成本从低到高排序:
- 添加标准兼容编译参数
在三个模块的项目属性→C/C→命令行中,添加/Zc:__cplusplus /permissive-两个编译参数,让MSVC采用更符合C标准的规则处理模板符号,大多数场景下无需修改业务代码即可解决问题。 - 开启链接时间代码生成(LTCG)
在三个模块的项目属性→常规→全程序优化,选择「使用链接时间代码生成(/GL)」;再进入链接器→优化→链接时间代码生成,选择「使用链接时间代码生成(/LTCG)」。MSVC链接器会在链接阶段自动合并重复的模板符号,解决冲突。 - 显式管控模板实例的导出导入
在Common.dll的公共头文件中添加如下声明:
再在Common.dll的任意一个cpp文件中添加显式实例化代码:#include <QMap> // 以下宏定义请替换为你项目中Common.dll原本的导出导入宏 #if defined(COMMON_LIB_BUILD) #define COMMON_API __declspec(dllexport) #else #define COMMON_API __declspec(dllimport) #endif extern template class COMMON_API QMap<int, double>;
该方案会强制所有模块用到的template class QMap<int, double>;QMap<int,double>实例统一从Common.dll导入,不会在各自编译单元生成重复符号,稳定性最高。
内容的提问来源于stack exchange,提问作者Rhoekath
相关产品推荐
相关产品推荐

