类内定义成员函数的静态变量链接规则及跨编译器行为问询
回答
问题1:是否存在G++编译选项可以复现MSVC的多静态变量实例行为
存在,你可以通过以下编译配置复现该行为:
- 编译动态库时添加编译选项
-fvisibility-inlines-hidden,链接时添加-Bsymbolic选项 - 编译主程序时也添加同样的
-fvisibility-inlines-hidden选项
该配置下,类内隐式内联的foo::bar函数符号会被标记为隐藏,动态链接时不会跨库合并,主程序和动态库会各自持有一份foo::bar的实例,对应的静态变量a也会存在两份,最终运行输出为0,和MSVC缺少__declspec(dllimport)的行为完全一致。
问题2:C++标准的相关规定
类内定义函数的实例化规则
类内直接定义的成员函数属于隐式内联函数,受C++的单定义规则(ODR)约束:整个程序中所有翻译单元看到的该函数定义必须完全一致,否则属于未定义行为。
静态变量处理的关联规则
标准明确规定:同一内联函数(含隐式内联)的所有定义中的局部静态对象,在整个程序中必须指向同一个实例。
你观测到的MSVC缺少__declspec(dllimport)时出现多实例的情况,属于Windows平台PE格式动态链接的实现特性,并不符合C++标准要求:PE格式默认不会跨DLL合并未显式标记导出/导入的内联函数符号,导致动态库和主程序各自生成了独立的foo::bar实例和对应的静态变量。
当你把foo::bar的定义移到lib.cpp中后,该函数不再是内联函数,整个程序只有一份定义在动态库中,自然只会生成一个静态变量实例,符合标准要求。
GCC默认的ELF动态链接行为是合并跨库的弱符号(内联函数属于弱符号),所以默认符合标准要求,只会生成一个静态变量实例。
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

