显式实例化模板的静态库强制链接及是否被有效使用的判断方法
前置问题修正
你当前的测试代码存在两个影响验证结果的问题,需要先调整:
- main函数中
A<2> foo();是函数声明,仅声明了一个返回值为A<2>、无参数的函数foo,并不会实际创建类对象、触发构造函数调用,自然不会引用静态库中的符号,需要修改为A<2> foo;才是创建类对象的写法。 - 类A的构造函数默认是private权限,外部无法调用,需要先修改为public权限才能正常创建对象。
判断静态库是否被正常使用的方法
方法1:移除静态库做对照编译
修正代码后,先执行不带静态库的编译命令:g++ main.cpp -I ./,如果编译时报*未定义引用(undefined reference)*到A<2>::A()的错误,说明编译器没有隐式实例化该模板,而是依赖外部提供的符号;此时再加上A.a编译可正常通过,就说明静态库中的显式实例化被正常使用了。方法2:查看链接过程与符号来源
编译时添加-v参数输出完整链接日志:g++ main.cpp A.a -I ./ -v,如果日志中出现链接器加载A.a中A.o目标文件来解析符号的记录,就说明静态库被正常调用。
也可在编译生成可执行文件后执行nm -C a.out查看符号表,能匹配到A<2>::A()的符号即表示对应实现已被正常链接。方法3:拆分模板声明与实现验证
将A.hpp中构造函数的实现移除,只保留声明:#ifndef A_HPP #define A_HPP template <int N> class A { public: A(); // 仅留声明,实现放在A.cpp中 }; #endif此时头文件中没有足够的信息让编译器隐式实例化
A<2>,如果项目仍可正常编译运行,就证明使用的是静态库中提前显式实例化的版本。
补充说明
编译时必须引入A.hpp是正常现象:头文件的作用是给编译器提供类型定义、函数声明等编译期必需的信息,静态库是链接期提供具体的符号实现,二者作用完全不同,不存在“用了静态库就不需要头文件”的说法。
注意:模板实例化的符号默认是弱符号(即你之前nm输出中的W标记),如果头文件中保留了模板的完整实现,编译器可能会在编译main.cpp时自行隐式实例化A<2>,此时即使你链接了静态库,链接器也可能选择本地实例化的符号而非库中的符号,这种情况建议用拆分声明实现的方式来强制依赖静态库的实例。
内容的提问来源于stack exchange,提问作者Jonas Hjulstad

