思考:为什么函数模板能够和函数重载放在一块。C++编译器是怎样提供函数模板机制的?
demo 1
#include <cstdio> #include <iostream> using namespace std; // 1.cpp // g++ -S 1.cpp -o 1.s template <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 ", x, y); } { char a = 'a'; char b = 'b'; myswap<char>(a, b); //1 函数模板 显示类型 调用 printf("a:%c b:%c ", a, b); } return 0; }
把demo 1编译成汇编文件,查看:
.file "1.cpp" .lcomm __ZStL8__ioinit,1,1 .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC0: .ascii "x:%d y:%d 12 " LC1: .ascii "a:%c b:%c 12 " .def ___gxx_personality_sj0; .scl 2; .type 32; .endef .def __Unwind_SjLj_Register; .scl 2; .type 32; .endef .def __Unwind_SjLj_Unregister; .scl 2; .type 32; .endef .text .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp movl %esp, %ebp pushl %edi pushl %esi pushl %ebx andl $-16, %esp subl $96, %esp movl $___gxx_personality_sj0, 52(%esp) movl $LLSDA959, 56(%esp) leal 60(%esp), %eax movl %ebp, (%eax) movl $L5, %edx movl %edx, 4(%eax) movl %esp, 8(%eax) leal 28(%esp), %eax movl %eax, (%esp) call __Unwind_SjLj_Register call ___main movl $10, 92(%esp) movl $20, 88(%esp) leal 88(%esp), %eax movl %eax, 4(%esp) leal 92(%esp), %eax movl %eax, (%esp) movl $1, 32(%esp) <span style="color:#ff0000;"> call __Z6myswapIiEvRT_S1_ // 41 call 117</span> movl 88(%esp), %edx movl 92(%esp), %eax movl %edx, 8(%esp) movl %eax, 4(%esp) movl $LC0, (%esp) call _printf movb $97, 87(%esp) movb $98, 86(%esp) leal 86(%esp), %eax movl %eax, 4(%esp) leal 87(%esp), %eax movl %eax, (%esp) movl $2, 32(%esp) <span style="color:#ff0000;"> call __Z6myswapIcEvRT_S1_ // 55 call 145</span> movb 86(%esp), %al movsbl %al, %edx movb 87(%esp), %al movsbl %al, %eax movl %edx, 8(%esp) movl %eax, 4(%esp) movl $LC1, (%esp) call _printf movl $0, %eax movl %eax, 24(%esp) jmp L8 L5: movl 36(%esp), %edx movl 32(%esp), %eax testl %eax, %eax je L6 cmpl $1, %eax je L7 .word 0x0b0f L6: movl %edx, %eax movl %eax, (%esp) movl $-1, 32(%esp) call __Unwind_SjLj_Resume L7: movl %edx, %eax movl %eax, (%esp) movl $-1, 32(%esp) call __Unwind_SjLj_Resume L8: leal 28(%esp), %eax movl %eax, (%esp) call __Unwind_SjLj_Unregister movl 24(%esp), %eax leal -12(%ebp), %esp popl %ebx popl %esi popl %edi popl %ebp ret .section .gcc_except_table,"w" LLSDA959: .byte 0xff .byte 0xff .byte 0x1 .uleb128 LLSDACSE959-LLSDACSB959 LLSDACSB959: .uleb128 0 .uleb128 0 .uleb128 0x1 .uleb128 0 LLSDACSE959: .text .section .rdata,"dr" .align 4 LC2: .ascii "hello ....316322312307304243260345272257312375 273266323255 calll 316322 " .section .text$_Z6myswapIiEvRT_S1_,"x" .linkonce discard .globl __Z6myswapIiEvRT_S1_ .def __Z6myswapIiEvRT_S1_; .scl 2; .type 32; .endef <span style="color:#ff0000;">__Z6myswapIiEvRT_S1_: // 117</span> pushl %ebp movl %esp, %ebp subl $40, %esp movl $0, -12(%ebp) movl 8(%ebp), %eax movl (%eax), %eax movl %eax, -12(%ebp) movl 12(%ebp), %eax movl (%eax), %edx movl 8(%ebp), %eax movl %edx, (%eax) movl 12(%ebp), %eax movl -12(%ebp), %edx movl %edx, (%eax) movl $LC2, 4(%esp) movl $__ZSt4cout, (%esp) call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp) movl %eax, %ecx call __ZNSolsEPFRSoS_E subl $4, %esp leave ret .section .text$_Z6myswapIcEvRT_S1_,"x" .linkonce discard .globl __Z6myswapIcEvRT_S1_ .def __Z6myswapIcEvRT_S1_; .scl 2; .type 32; .endef <span style="color:#ff0000;">__Z6myswapIcEvRT_S1_: // 145</span> pushl %ebp movl %esp, %ebp subl $40, %esp movb $0, -9(%ebp) movl 8(%ebp), %eax movb (%eax), %al movb %al, -9(%ebp) movl 12(%ebp), %eax movb (%eax), %dl movl 8(%ebp), %eax movb %dl, (%eax) movl 12(%ebp), %eax movb -9(%ebp), %dl movb %dl, (%eax) movl $LC2, 4(%esp) movl $__ZSt4cout, (%esp) call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp) movl %eax, %ecx call __ZNSolsEPFRSoS_E subl $4, %esp leave ret .text .def ___tcf_0; .scl 3; .type 32; .endef ___tcf_0: pushl %ebp movl %esp, %ebp subl $8, %esp movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitD1Ev leave ret .def __Z41__static_initialization_and_destruction_0ii; .scl 3; .type 32; .endef __Z41__static_initialization_and_destruction_0ii: pushl %ebp movl %esp, %ebp subl $24, %esp cmpl $1, 8(%ebp) jne L12 cmpl $65535, 12(%ebp) jne L12 movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitC1Ev movl $___tcf_0, (%esp) call _atexit L12: leave ret .def __GLOBAL__sub_I_main; .scl 3; .type 32; .endef __GLOBAL__sub_I_main: pushl %ebp movl %esp, %ebp subl $24, %esp movl $65535, 4(%esp) movl $1, (%esp) call __Z41__static_initialization_and_destruction_0ii leave ret .section .ctors,"w" .align 4 .long __GLOBAL__sub_I_main .def __Unwind_SjLj_Resume; .scl 2; .type 32; .endef .def _printf; .scl 2; .type 32; .endef .def __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_; .scl 2; .type 32; .endef .def __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc; .scl 2; .type 32; .endef .def __ZNSolsEPFRSoS_E; .scl 2; .type 32; .endef .def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef .def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef .def _atexit; .scl 2; .type 32; .endef观察发现一个现象,myswap函数模版有一个声明,两个定义,这样的情况和我在“为什么会有函数模版中”博文中提到的两个myswap函数非常相似,实际这里体现了C++实现函数模版的本。本来须要程序猿依据须要去写非常多个逻辑同样,參数不同的函数。可是C++编译器帮我们做了这件事,依据调用会自己主动生成这些函数。这也是为什么函数模版能够和普通函数放在一起。
总结:函数模版机制结论:
编译器并非把函数模版处理成可以处理随意类的函数;
编译器从函数模版通过详细类型产生不同的函数;
编译器会对函数模版进行两次编译:在声明的地方对模版代码本身进行编译,在调用的地方对參数替换后的代码进行编译。