?思考:为什么函数模板可以和函数重载放在一起。C++编译器是如何提供函数模板机制的?
实例
#include<iostream>
using namespace std;
template<typename T>
void fun(T& a, T& b) {
cout << "a" << a << "-b: " << b << endl;
cout << "我是模板函数" << endl;
}
void fun(int a, char b) {
cout << "a" << a << "-b: " << b << endl;
cout << "我是函数调用" << endl;
}
int main() {
int a = 1;
char b = 'z';
fun(a, b);//普通函数调用,可以进行隐式的类型转换
fun(b, a);// 普通函数调用
fun(a, a);//调用函数模板(本质:参数类型化),严格按照类型匹配,不会进行类型自动转换
return 0;
}
编译
使用g++ test.c -S
生成test.s文件,内容如下:
.file "test.cpp"
.lcomm _ZStL8__ioinit,1,1
.section .rdata,"dr"
.LC0:
.ascii "a\0"
.LC1:
.ascii "-b: \0"
.LC2:
.ascii "\316\322\312\307\272\257\312\375\265\367\323\303\0"
.text
.globl _Z3funic
.def _Z3funic; .scl 2; .type 32; .endef
.seh_proc _Z3funic
_Z3funic:
.LFB1049:
pushq %rbp
.seh_pushreg %rbp
pushq %rbx
.seh_pushreg %rbx
subq $40, %rsp
.seh_stackalloc 40
leaq 128(%rsp), %rbp
.seh_setframe %rbp, 128
.seh_endprologue
movl %ecx, -64(%rbp)
movl %edx, %eax
movb %al, -56(%rbp)
movsbl -56(%rbp), %ebx
leaq .LC0(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl -64(%rbp), %edx
movq %rax, %rcx
call _ZNSolsEi
leaq .LC1(%rip), %rdx
movq %rax, %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %ebx, %edx
movq %rax, %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
leaq .LC2(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
nop
addq $40, %rsp
popq %rbx
popq %rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
.LFB1050:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $1, -8(%rbp)
movb $122, -1(%rbp)
movsbl -1(%rbp), %edx
movl -8(%rbp), %eax
movl %eax, %ecx
call _Z3funic
movl -8(%rbp), %eax
movsbl %al, %edx
movsbl -1(%rbp), %eax
movl %eax, %ecx
call _Z3funic
leaq -8(%rbp), %rdx
leaq -8(%rbp), %rax
movq %rax, %rcx
call _Z3funIiEvRT_S1_
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.section .rdata,"dr"
.LC3:
.ascii "\316\322\312\307\304\243\260\345\272\257\312\375\0"
.section .text$_Z3funIiEvRT_S1_,"x"
.linkonce discard
.globl _Z3funIiEvRT_S1_
.def _Z3funIiEvRT_S1_; .scl 2; .type 32; .endef
.seh_proc _Z3funIiEvRT_S1_
_Z3funIiEvRT_S1_:
.LFB1055:
pushq %rbp
.seh_pushreg %rbp
pushq %rsi
.seh_pushreg %rsi
pushq %rbx
.seh_pushreg %rbx
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 32(%rbp)
movq %rdx, 40(%rbp)
movq 40(%rbp), %rax
movl (%rax), %ebx
movq 32(%rbp), %rax
movl (%rax), %esi
leaq .LC0(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %esi, %edx
movq %rax, %rcx
call _ZNSolsEi
leaq .LC1(%rip), %rdx
movq %rax, %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %ebx, %edx
movq %rax, %rcx
call _ZNSolsEi
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
leaq .LC3(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
nop
addq $32, %rsp
popq %rbx
popq %rsi
popq %rbp
ret
.seh_endproc
.text
.def __tcf_0; .scl 3; .type 32; .endef
.seh_proc __tcf_0
__tcf_0:
.LFB1062:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
leaq _ZStL8__ioinit(%rip), %rcx
call _ZNSt8ios_base4InitD1Ev
nop
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def _Z41__static_initialization_and_destruction_0ii; .scl 3; .type 32; .endef
.seh_proc _Z41__static_initialization_and_destruction_0ii
_Z41__static_initialization_and_destruction_0ii:
.LFB1061:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movl %ecx, 16(%rbp)
movl %edx, 24(%rbp)
cmpl $1, 16(%rbp)
jne .L6
cmpl $65535, 24(%rbp)
jne .L6
leaq _ZStL8__ioinit(%rip), %rcx
call _ZNSt8ios_base4InitC1Ev
leaq __tcf_0(%rip), %rcx
call atexit
nop
.L6:
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def _GLOBAL__sub_I__Z3funic; .scl 3; .type 32; .endef
.seh_proc _GLOBAL__sub_I__Z3funic
_GLOBAL__sub_I__Z3funic:
.LFB1063:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movl $65535, %edx
movl $1, %ecx
call _Z41__static_initialization_and_destruction_0ii
nop
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.section .ctors,"w"
.align 8
.quad _GLOBAL__sub_I__Z3funic
.ident "GCC: (tdm64-1) 4.9.2"
.def _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc; .scl 2; .type 32; .endef
.def _ZNSolsEi; .scl 2; .type 32; .endef
.def _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c; .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
.section .rdata$.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, "dr"
.globl .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
.linkonce discard
.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_:
.quad _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
.section .rdata$.refptr._ZSt4cout, "dr"
.globl .refptr._ZSt4cout
.linkonce discard
.refptr._ZSt4cout:
.quad _ZSt4cout
函数模板机制结论
- 编译器并不是把函数模板处理成能够处理任意类的函数
- 编译器从函数模板通过具体类型产生不同的函数
- 编译器会对函数模板进行<mark>两次编译</mark>
在声明的地方对模板代码本身进行编译
在调用的地方对参数替换后的代码进行编译
其中的两次编译发生了什么?
- 第一个编译根据函数模板进行词法分析、语法分析、句法分析产生一个简要的函数模板类型
- d第二次编译根据调用产生一个具体的函数原型,若还有新的调用,则再产生一个新的函数原型,并将函数原型放在test.s中