理解函数模板
模板的意义:对函数类型可以做修改
- 函数模板:bool compare(T a, T b)
- 模板实例化:定义一个模板参数类型,进行一次函数的实例化
- 模板函数:一个函数模板的实例化就是一个模板函数
- 模板类型参数:T
- 模板非类型参数:
- 模板的实参推演:根据实参反推模板参数类型
- 模板的特例化:为函数模板的特殊参数类型进行特殊设置
- 对于某些类型来说,依赖模板的函数逻辑是有问题的。需要模板的特例化,特例化不是编译器提供的,而是用户提供的一种实例化方法,
- 函数模板 模板的特例化 非模板(普通)函数的重载关系:如下例三个字符串的compare函数,一般优先调用非模板函数,如果非模板函数不存在,才会调用特例化模板函数或模板函数。
#include <iostream>
#include <cstring>
using namespace std;
template<typename T> //定义一个模板类型参数列表,也可以定义为类
bool compare(T a, T b) { //compare是一个函数模板
cout << "template" << endl;
return a > b;
}
template<> //模板的特例化:针对compare函数模板,提供一个给const char*的特例化版本
bool compare(const char *a, const char *b) { //compare是一个函数模板
cout << "str template" << endl;
return strcmp(a, b) > 0;
}
//compare是一个函数模板
//在函数调用点,编译器用用户指定的类型,从原模版实例化一份函数代码
//从函数模板实例化出来的函数为模板函数,如下
//符号表会依据函数名和参数类型为每个模板函数加入新符号,模板函数才是真正被编译的函数,而函数模板不是
//bool compare<int>(int a, int b) {
// cout << "template" << endl;
// return a > b;
//}
//bool compare<double>(double a, double b) {
// cout << "template" << endl;
// return a > b;
//}
int main() {
//函数的调用点
compare<int>(10, 20);
compare<double>(10.0, 20.0);
compare(10, 20); //函数模板的实参推演:根据用户传入的实参类型,反推出模板所使用的类型参数
//第二次使用compare<int>时不需要再次产生模板函数,直接调用第一次产生的函数即可
//compare(10, 20.0); //报错,无法确定模板参数类型
compare("aa", "bb"); //可以执行,默认实参自动推演为const char*,从而比较两个指针地址的大小。
//如果进行字符串的字典比较,如何?
return 0;
}
- 如果将函数模板定义在另一个文件中定义,会出现问题。因为模板本身不会编译,模板函数才会编译。如果A文件中调用了B文件中的模板函数,事实上在编译汇编阶段,B文件并不会编译产生A所需的模板函数,符号表中也不会产生对应符号,因此在链接阶段,A文件所需使用的几个*UND*函数符号无法在B中找到。不过,B中定义的普通函数和特例化模板函数在A中是可以被看到的。因此模板源代码一般都放在头文件中,在源文件中#include直接展开。
- 如果在调用之前,声明函数模板对应类型的模板函数,也可以避免上述情况,一般不这样。