C++编译器函数模版机制剖析 - 函数模版的本质

来源:互联网 发布:怎么改淘宝店铺名字 编辑:程序博客网 时间:2024/06/10 07:43

思考:为什么函数模板可以和函数重载放在一块。C++编译器是如何提供函数模板机制的?

demo 1

#include <cstdio>#include <iostream>using namespace std;// 1.cpp// g++ -S 1.cpp  -o 1.stemplate <typename T>void myswap(T &a, T &b){T c = 0;c = a;a = b;b = c;cout << "hello ....我是模板函数 欢迎 calll 我" << endl;}int main(){{int x = 10; int y = 20;myswap<int>(x, y); //1 函数模板 显示类型 调用printf("x:%d y:%d \n", x, y);}{char a = 'a'; char b = 'b';myswap<char>(a, b); //1 函数模板 显示类型 调用printf("a:%c b:%c \n", a, b);}return 0;}

把demo 1编译成汇编文件,查看:

.file"1.cpp".lcomm __ZStL8__ioinit,1,1.def___main;.scl2;.type32;.endef.section .rdata,"dr"LC0:.ascii "x:%d y:%d \12\0"LC1:.ascii "a:%c b:%c \12\0".def___gxx_personality_sj0;.scl2;.type32;.endef.def__Unwind_SjLj_Register;.scl2;.type32;.endef.def__Unwind_SjLj_Unregister;.scl2;.type32;.endef.text.globl_main.def_main;.scl2;.type32;.endef_main:pushl%ebpmovl%esp, %ebppushl%edipushl%esipushl%ebxandl$-16, %espsubl$96, %espmovl$___gxx_personality_sj0, 52(%esp)movl$LLSDA959, 56(%esp)leal60(%esp), %eaxmovl%ebp, (%eax)movl$L5, %edxmovl%edx, 4(%eax)movl%esp, 8(%eax)leal28(%esp), %eaxmovl%eax, (%esp)call__Unwind_SjLj_Registercall___mainmovl$10, 92(%esp)movl$20, 88(%esp)leal88(%esp), %eaxmovl%eax, 4(%esp)leal92(%esp), %eaxmovl%eax, (%esp)movl$1, 32(%esp)<span style="color:#ff0000;">call__Z6myswapIiEvRT_S1_ // 41 call 117</span>movl88(%esp), %edxmovl92(%esp), %eaxmovl%edx, 8(%esp)movl%eax, 4(%esp)movl$LC0, (%esp)call_printfmovb$97, 87(%esp)movb$98, 86(%esp)leal86(%esp), %eaxmovl%eax, 4(%esp)leal87(%esp), %eaxmovl%eax, (%esp)movl$2, 32(%esp)<span style="color:#ff0000;">call__Z6myswapIcEvRT_S1_ // 55 call 145</span>movb86(%esp), %almovsbl%al, %edxmovb87(%esp), %almovsbl%al, %eaxmovl%edx, 8(%esp)movl%eax, 4(%esp)movl$LC1, (%esp)call_printfmovl$0, %eaxmovl%eax, 24(%esp)jmpL8L5:movl36(%esp), %edxmovl32(%esp), %eaxtestl%eax, %eaxjeL6cmpl$1, %eaxjeL7.word0x0b0fL6:movl%edx, %eaxmovl%eax, (%esp)movl$-1, 32(%esp)call__Unwind_SjLj_ResumeL7:movl%edx, %eaxmovl%eax, (%esp)movl$-1, 32(%esp)call__Unwind_SjLj_ResumeL8:leal28(%esp), %eaxmovl%eax, (%esp)call__Unwind_SjLj_Unregistermovl24(%esp), %eaxleal-12(%ebp), %esppopl%ebxpopl%esipopl%edipopl%ebpret.section.gcc_except_table,"w"LLSDA959:.byte0xff.byte0xff.byte0x1.uleb128 LLSDACSE959-LLSDACSB959LLSDACSB959:.uleb128 0.uleb128 0.uleb128 0x1.uleb128 0LLSDACSE959:.text.section .rdata,"dr".align 4LC2:.ascii "hello ....\316\322\312\307\304\243\260\345\272\257\312\375 \273\266\323\255 calll \316\322\0".section.text$_Z6myswapIiEvRT_S1_,"x".linkonce discard.globl__Z6myswapIiEvRT_S1_.def__Z6myswapIiEvRT_S1_;.scl2;.type32;.endef<span style="color:#ff0000;">__Z6myswapIiEvRT_S1_: // 117</span>pushl%ebpmovl%esp, %ebpsubl$40, %espmovl$0, -12(%ebp)movl8(%ebp), %eaxmovl(%eax), %eaxmovl%eax, -12(%ebp)movl12(%ebp), %eaxmovl(%eax), %edxmovl8(%ebp), %eaxmovl%edx, (%eax)movl12(%ebp), %eaxmovl-12(%ebp), %edxmovl%edx, (%eax)movl$LC2, 4(%esp)movl$__ZSt4cout, (%esp)call__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcmovl$__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)movl%eax, %ecxcall__ZNSolsEPFRSoS_Esubl$4, %espleaveret.section.text$_Z6myswapIcEvRT_S1_,"x".linkonce discard.globl__Z6myswapIcEvRT_S1_.def__Z6myswapIcEvRT_S1_;.scl2;.type32;.endef<span style="color:#ff0000;">__Z6myswapIcEvRT_S1_: // 145</span>pushl%ebpmovl%esp, %ebpsubl$40, %espmovb$0, -9(%ebp)movl8(%ebp), %eaxmovb(%eax), %almovb%al, -9(%ebp)movl12(%ebp), %eaxmovb(%eax), %dlmovl8(%ebp), %eaxmovb%dl, (%eax)movl12(%ebp), %eaxmovb-9(%ebp), %dlmovb%dl, (%eax)movl$LC2, 4(%esp)movl$__ZSt4cout, (%esp)call__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcmovl$__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)movl%eax, %ecxcall__ZNSolsEPFRSoS_Esubl$4, %espleaveret.text.def___tcf_0;.scl3;.type32;.endef___tcf_0:pushl%ebpmovl%esp, %ebpsubl$8, %espmovl$__ZStL8__ioinit, %ecxcall__ZNSt8ios_base4InitD1Evleaveret.def__Z41__static_initialization_and_destruction_0ii;.scl3;.type32;.endef__Z41__static_initialization_and_destruction_0ii:pushl%ebpmovl%esp, %ebpsubl$24, %espcmpl$1, 8(%ebp)jneL12cmpl$65535, 12(%ebp)jneL12movl$__ZStL8__ioinit, %ecxcall__ZNSt8ios_base4InitC1Evmovl$___tcf_0, (%esp)call_atexitL12:leaveret.def__GLOBAL__sub_I_main;.scl3;.type32;.endef__GLOBAL__sub_I_main:pushl%ebpmovl%esp, %ebpsubl$24, %espmovl$65535, 4(%esp)movl$1, (%esp)call__Z41__static_initialization_and_destruction_0iileaveret.section.ctors,"w".align 4.long__GLOBAL__sub_I_main.def__Unwind_SjLj_Resume;.scl2;.type32;.endef.def_printf;.scl2;.type32;.endef.def__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_;.scl2;.type32;.endef.def__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc;.scl2;.type32;.endef.def__ZNSolsEPFRSoS_E;.scl2;.type32;.endef.def__ZNSt8ios_base4InitD1Ev;.scl2;.type32;.endef.def__ZNSt8ios_base4InitC1Ev;.scl2;.type32;.endef.def_atexit;.scl2;.type32;.endef
观察发现一个现象,myswap函数模版有一个声明,两个定义,这种情况和我在“为什么会有函数模版中”博文中提到的两个myswap函数很相似,实际这里体现了C++实现函数模版的本,本来需要程序员根据需要去写很多个逻辑相同,参数不同的函数,但是C++编译器帮我们做了这件事,根据调用会自动生成这些函数,这也是为什么函数模版可以和普通函数放在一起。

总结:函数模版机制结论:

编译器并不是把函数模版处理成能够处理任意类的函数;

编译器从函数模版通过具体类型产生不同的函数;

编译器会对函数模版进行两次编译:在声明的地方对模版代码本身进行编译,在调用的地方对参数替换后的代码进行编译。



0 0