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

简介:

思考:为什么函数模板能够和函数重载放在一块。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++编译器帮我们做了这件事,依据调用会自己主动生成这些函数。这也是为什么函数模版能够和普通函数放在一起。

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

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

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

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





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5336758.html,如需转载请自行联系原作者

相关文章
|
4天前
|
算法 安全 编译器
【C++】从零开始认识泛型编程 — 模版
泛型编程是C++中十分关键的一环,泛型编程是C++编程中的一项强大功能,它通过模板提供了类型无关的代码,使得C++程序可以更加灵活和高效,极大的简便了我们编写代码的工作量。
13 3
|
4天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
30 1
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
|
5天前
|
存储 算法 对象存储
【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]
【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]
14 1
|
5天前
|
存储 算法 数据安全/隐私保护
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
【C++入门到精通】 哈希结构 | 哈希冲突 | 哈希函数 | 闭散列 | 开散列 [ C++入门 ]
7 0
|
5天前
|
存储 自然语言处理 C++
刷题用到的非常有用的函数c++(持续更新)
刷题用到的非常有用的函数c++(持续更新)
14 1
|
6天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
21 1
|
12天前
|
存储 C++
c/c++宏定义(函数)
c/c++宏定义(函数)
|
14天前
|
存储 C++
C++ 异常处理机制详解:轻松掌握异常处理技巧
C++ 异常处理提供结构化错误管理,增强程序健壮性。通过`throw`抛出异常,`try-catch`捕获并处理。示例展示了当年龄小于18时抛出异常。优点包括提高健壮性和代码可维护性,但可能降低性能并复杂化代码。另外,介绍了四种在C++中相加两个数的方法,包括使用运算符、函数、类、STL函数和lambda表达式。
20 0
|
5天前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
16 0