您的位置:首页 > 编程语言 > C语言/C++

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

2016-03-30 13:06 585 查看
思考:为什么函数模板能够和函数重载放在一块。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 \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;	.scl	2;	.type	32;	.endef
.section .rdata,"dr"
LC0:
.ascii "x:%d y:%d \12\0"
LC1:
.ascii "a:%c b:%c \12\0"
.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 ....\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_;	.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++编译器帮我们做了这件事,依据调用会自己主动生成这些函数。这也是为什么函数模版能够和普通函数放在一起。

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

编译器并非把函数模版处理成可以处理随意类的函数;

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

编译器会对函数模版进行两次编译:在声明的地方对模版代码本身进行编译,在调用的地方对參数替换后的代码进行编译。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: