標籤:c++ 編譯器 函數模版 本質
思考:為什麼函數模板可以和函數重載放在一塊。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++編譯器幫我們做了這件事,根據調用會自動產生這些函數,這也是為什麼函數模版可以和普通函數放在一起。
總結:函數模版機制結論:
編譯器並不是把函數模版處理成能夠處理任意類的函數;
編譯器從函數模版通過具體類型產生不同的函數;
編譯器會對函數模版進行兩次編譯:在聲明的地方對模版代碼本身進行編譯,在調用的地方對參數替換後的代碼進行編譯。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
C++編譯器函數模版機制剖析 - 函數模版的本質