C++ STL中vector等模板实例化后的代码存放在哪里?
问题描述
我有一段使用C++ vector的极简示例代码如下:
#include <iostream> #include <vector> using namespace std; int main() { vector<int> g1; return 0; }
将上述代码放到在线汇编查看平台编译后,得到如下生成的汇编代码:
main: stp x29, x30, [sp, -64]! mov x29, sp str x19, [sp, 16] add x0, sp, 40 bl std::vector<int, std::allocator<int> >::vector() [complete object constructor] mov w19, 0 add x0, sp, 40 bl std::vector<int, std::allocator<int> >::~vector() [complete object destructor] mov w0, w19 ldr x19, [sp, 16] ldp x29, x30, [sp], 64 ret __static_initialization_and_destruction_0(int, int): stp x29, x30, [sp, -32]! mov x29, sp str w0, [sp, 28] str w1, [sp, 24] ldr w0, [sp, 28] cmp w0, 1 bne .L27 ldr w1, [sp, 24] mov w0, 65535 cmp w1, w0 bne .L27 adrp x0, _ZStL8__ioinit add x0, x0, :lo12:_ZStL8__ioinit bl std::ios_base::Init::Init() [complete object constructor] adrp x0, __dso_handle add x2, x0, :lo12:__dso_handle adrp x0, _ZStL8__ioinit add x1, x0, :lo12:_ZStL8__ioinit adrp x0, _ZNSt8ios_base4InitD1Ev add x0, x0, :lo12:_ZNSt8ios_base4InitD1Ev bl __cxa_atexit .L27: nop ldp x29, x30, [sp], 32 ret _GLOBAL__sub_I_main: stp x29, x30, [sp, -16]! mov x29, sp mov w1, 65535 mov w0, 1 bl __static_initialization_and_destruction_0(int, int) ldp x29, x30, [sp], 16 ret DW.ref.__gxx_personality_v0: .xword __gxx_personality_v0
我的核心疑问:std::vector<int, std::allocator<int>>::vector() 构造函数的代码可以在哪里找到?
我做了自定义模板类的对照测试,示例代码如下:
#include <iostream> using namespace std; template <class T, class U> class A { T x; U y; public: A() { cout << "Constructor Called" << endl; } }; int main() { A<char, char> a; A<int, double> b; return 0; }
我清楚A<char, char>和A<int, double>实例化后的代码会生成在自身代码对应的目标文件中,但C++ STL的模板类、模板函数,它们实例化生成的目标代码存放在什么位置?
我知道C标准库有对应的库文件比如*libstdc.so*,但编译器并不会把STL模板实例化的代码生成到这个.so文件内部,想搞清楚背后的机制。
解答
- 你在汇编输出里只看到vector构造函数的调用、看不到对应实现,核心原因是默认编译配置开启了优化:空vector的默认构造、析构逻辑极其简单,构造只需要把内部存储首尾指针、容量指针初始化为空,析构时因为容器没有元素,不需要执行元素销毁、内存释放操作。这类简单的STL模板函数会被编译器标记为弱符号,实例化出的代码最终要么直接被内联、消掉显式函数调用,要么在链接阶段和其他编译单元生成的同名实例合并,全局只保留一份代码。
- STL模板的实现全部写在头文件中,以GCC配套的libstdc为例,vector默认构造函数的源码就在编译器安装目录下的
bits/stl_vector.h文件里。C模板本身不支持分离编译,所有你代码里用到的STL模板实例,都是在编译你自己代码的过程中,直接从这些头文件读取源码完成实例化的,通用模板代码本来就不会提前编译放到libstdc++.so里。 - libstdc++.so里存储的都是不依赖模板参数、可以提前预编译的固定逻辑:比如IO流的全局状态初始化代码、异常处理的公共运行时逻辑、全局
new/delete的实现、RTTI相关的基础函数,还有部分提前做了显式特化的模板实现。 - 你可以自行验证:编译那段空vector的测试代码时加上
-O0参数关闭所有优化,再用objdump -d反汇编生成的可执行文件,就能直接在可执行文件的代码段里找到vector构造、析构的完整汇编——这些代码都是编译你的main文件时,直接从vector头文件实例化生成、写入你自己的目标文件的。 - 平时你感觉这些代码不在自己的目标文件里,是因为开启优化后,编译器会对STL模板生成的代码做COMDAT段去重、内联、无用代码消除:哪怕项目里10个源文件都实例化了
vector<int>的默认构造,最终链接时也只会保留一份代码,不会产生冗余。 - 还有个编译优化细节需要说明:libstdc会对高频使用的STL模板实例做预编译显式实例化,比如
vector<int>的基础成员函数、std::string(即basic_string<char>)的常用方法,这些提前实例化好的代码会存放在libstdc.so里。编译你的代码时,编译器如果检测到对应实例已经有预编译版本,就不会在你的目标文件里重复生成代码,直接链接调用so里的现成实现。这个优化纯粹是为了加快编译速度、减小目标文件体积,不代表模板本身是提前编译到so里的——如果使用不常见的模板参数,比如vector<你自定义的某个类>,对应的实例化代码还是会生成在你自己的目标文件中。
内容的提问来源于stack exchange,提问作者Simon Zhu
相关产品推荐
相关产品推荐

