c++函数模板和运行机制

C++_template

c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。

  • c++提供两种模板机制:函数模板和类模板
  • 类属:类型参数化,又称参数模板

函数模板

函数模板是 C++ 中的一种特性,它允许你编写一个通用的函数,能够处理不同类型的数据,而不需要针对每种类型编写多个函数。通过函数模板,你可以编写一次代码,然后使用不同的数据类型来实例化该函数,从而生成针对不同数据类型的具体函数。

函数模板可以处理各种不同的数据类型,只要这些数据类型支持所执行的操作。例如,你可以使用函数模板来编写一个通用的排序函数,它可以对整数、浮点数、字符串等进行排序,而无需为每种类型编写一个特定的排序函数。

假设现在有几个类似函数:

void SwapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

void SwapChar(char& a, char& b) {
	char temp = a;
	a = b;
	b = temp;
}

如果要交换double,bool等其他类型时,需要重复写很多个功能相同的函数。写的函数越多,当交换逻辑发生变化的时候,所有的函数都需要修改,无形当中增加了代码的维护难度。

template<class T>

void Swap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
void test() {
	int a = 10;
	int b = 20;
	cout << "a:" << a << " b:" << b << endl;
	//1. 这里有个需要注意点,函数模板可以自动推导参数的类型
	Swap(a, b);
	cout << "a:" << a << " b:" << b << endl;
	cout << "-----------------\n";
	char c1 = 'a';
	char c2 = 'b';
	cout << "c1:" << c1 << " c2:" << c2 << endl;
	//2. 函数模板可以自动类型推导,那么也可以显式指定类型
	Swap<char>(c1, c2);
	cout << "c1:" << c1 << " c2:" << c2 << endl;
}

image-20240419092905080

普通函数和函数模板区别

  1. 定义
    • 普通函数:定义时指定了具体的参数类型和返回类型,如 int add(int a, int b)
    • 函数模板:定义时使用关键字 template 并在尖括号中声明模板参数,如 template <typename T> T add(T a, T b)
  2. 参数类型
    • 普通函数:参数类型是固定的,无法处理不同类型的参数。
    • 函数模板:参数类型可以是模板参数,因此可以处理不同类型的参数。
  3. 具体化
    • 普通函数:只有一份具体的实现,不支持根据参数类型动态生成多个版本。
    • 函数模板:可以根据不同的模板参数生成多个具体化的版本,以应对不同的参数类型。
  4. 类型推断
    • 普通函数:参数类型必须显式指定。
    • 函数模板:可以通过函数参数的类型自动推断模板参数的类型。
  5. 使用
    • 普通函数:直接调用,参数类型必须与函数声明中指定的类型相匹配。
    • 函数模板:使用时可以通过显式指定模板参数的方式实例化模板,也可以让编译器自动推断模板参数的类型。

函数模板和普通函数在一起调用规则

  1. c++编译器优先考虑普通函数
  2. 可以通过空模板实参列表的语法限定编译器只能通过模板匹配
  3. 函数模板可以像普通函数那样可以被重载
  4. 如果函数模板可以产生一个更好的匹配,那么选择模板
template<class T>
T MyPlus(T a, T b) {
	T ret = a + b;
	return ret;
}
//普通函数
int MyPlus(int a, int b) {
	int ret = a + b;
	return ret;
}
void test() {
	int a = 10;
	int b = 20;
	char c = 'a';
	char d = 'b';
	//如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数
	cout << MyPlus(a, b) << endl;
	//如果我必须要调用函数模板,那么怎么办?
	cout << MyPlus<>(a, b) << endl;
	//此时普通函数也可以匹配,因为普通函数可以自动类型转换
	//但是此时函数模板能够有更好的匹配
	//如果函数模板可以产生一个更好的匹配,那么选择模板
	cout << MyPlus(c, d);
}

image-20240419094504835

为什么函数模板可以和普通函数放在一起?c++编译器是如何实现函数模板机制的?

hello.cpp程序是高级c语言程序,这种程序易于被人读懂。为了在系统上运行hello.c程序,每一条c语句都必须转化为低级的机器指令。然后将这些机器指令打包成可执行目标文件格式,并以二进制形式存储于磁盘中。

预处理(Pre-processing) -> 编译(Compiling) ->汇编(Assembling) -> 链接(Linking)

image-20240419095156067

函数模板机制结论:

  • 编译器并不是把函数模板处理成能够处理任何类型的函数: 函数模板本身并不是完整的可执行代码,而是一种通用的模板。编译器在编译过程中不会将函数模板直接处理成能够处理任何类型的函数,而是根据模板定义和实际使用情况生成特定类型的函数代码。这意味着函数模板并不直接对应于实际可执行的函数代码,而是在使用时根据模板参数生成相应的函数代码。
  • 函数模板通过具体类型产生不同的函数: 当程序中使用函数模板并提供具体类型的参数时,编译器会根据模板定义生成针对这些具体类型的函数代码。这个过程称为模板实例化。换句话说,函数模板在编译过程中会根据模板参数的具体类型产生不同的函数实现,以适应不同类型的参数。
  • 编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译: 这是指编译器在处理函数模板时的两个重要阶段:模板声明和模板实例化。在模板声明阶段,编译器会对函数模板的语法进行检查,并确保模板定义的正确性。在模板实例化阶段,编译器会根据模板参数的具体类型,对模板代码进行替换并生成实际的函数代码。这两个阶段都是在编译过程中发生的,但它们的重点和操作对象略有不同。
posted @ 2024-04-19 10:01  ivanlee717  阅读(78)  评论(0编辑  收藏  举报