CMake add_library如何决定符号可见性?模板特化符号问题咨询
关于CMake构建共享库的符号可见性与模板特化未定义引用问题
默认符号可见性的判定规则
在类Unix环境(使用GCC、Clang这类编译器)下,默认的符号可见性完全遵循C++的链接属性规则,nm工具里的大写T(全局可见函数)和小写t(内部可见函数)对应以下情况:
- 全局可见(
T):所有不带static关键字的全局函数、全局变量,以及类的非静态成员函数(只要是公开/受保护且未被static修饰),默认都会被标记为全局可见,会导出到共享库的动态符号表中,其他模块可以链接到这些符号。 - 内部可见(
t):满足以下任意条件的符号会被限制在当前编译单元(.o文件)内部访问,不会导出到共享库:- 用
static关键字修饰的全局函数/变量 - 位于匿名命名空间(
namespace { ... })内的所有实体 - 局部静态变量(不过这类符号通常用
b/B标记,而非t)
- 用
顺便提一句:MSVC的默认行为不同,它默认只导出带__declspec(dllexport)标记的符号,但你用nm检查,显然是在类Unix环境下,所以重点看上面的规则。
模板函数特化导致符号内部可见的成因
模板特化的情况有点特殊,核心要记住:显式模板特化本质上是一个普通的函数/类,它的链接规则和非模板实体完全一致。你的模板特化符号变成t,大概率是以下原因之一:
- 特化被放在匿名命名空间中:如果你的显式特化是写在某个
.cpp文件的匿名命名空间里,比如:
匿名命名空间里的所有内容默认都是内部链接,编译器会把这个特化符号标记为namespace { template<> void my_template_func<int>() { // 特化实现 } }t,仅当前编译单元可见,不会导出到共享库。 - 特化被
static修饰:如果给显式特化加上了static关键字,同样会把它变成内部链接符号,只能在当前编译单元使用:static template<> void my_template_func<int>() { // 特化实现 } - 未在头文件中声明特化:如果你的显式特化只在
.cpp文件里定义,没有在对应的头文件中对外声明这个特化的存在,编译器可能会认为这个特化仅当前单元需要,进而将其标记为内部可见(不过这种情况相对少见,除非开启了特定优化选项)。
当你把共享库链接到其他项目部分时,链接器需要找到全局可见的符号,但这个特化符号是t(内部可见),根本没出现在共享库的动态符号表中,自然会报“未定义引用”错误。
内容的提问来源于stack exchange,提问作者Ben Farmer
相关产品推荐
相关产品推荐

