持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
函数重载
从函数重载说起,然后引出我们今天主角函数模板。
swap(i,j)
用于将 i 和 j 进行交互的一个函数,swap 函数可以适合很多类型,也是我们在介绍函数重载时通常会提及到例子。
swap(int &a, int &b)
swap(string &a, string &b)
每调用函数时,编译器会根据调用函数参数的类型来选择对应的函数。
int i,j;
int s,t;
swap(i,j)
在调用 swap 使用整型参数 i,j 就会调用 swap(int&, int&) ,但是当 swap(s,j)没有根据参数匹配的函数就会抛出错误。
接下来看 swap 函数的实现
void swap(int &a,int &b)
{
int temp{a};
a = b;
b = temp;
}
void swap(string &a,string &b)
{
string temp{a};
a = b;
b = temp;
}
- 函数重载可以让用户通过一个函数名可以使用多个函数
- 这些函数执行的相同的业务逻辑
- 重载可以产生更易于使用的库,也就是易于使用
那么函数重载是否也存在问题,当然了,接下来列出函数重载的问题
- 然而,重载不可避免为每个几乎相同的函数编写代码,其实这可能存在潜在问题,因为一旦有更改就要进行多处修改。
函数模板
还我们再来看函数模板,函数模板是一个算法的泛化,并不是一个真正的函数。函数模板并非函数。 接下来我们可以定义函数模板来实现上面函数重载功能。其实函数模板可看做制作函数的配方。
using namespace std;
template<typename T>
void swap(T &a, T &b)
- 这里声明了一个函数模板,在函数模板通过
<>在其中指定了类型变量,随后在函数中可以使用 T 来作为参数类型使用,也就是给编译器提供了调用函数时,如何生成函数的所需要信息。
template<typename T>
void swap2(T &a, T &b)
{
T temp{a};
a = b;
b = temp;
}
int main()
{
int a = 2;
int b = 3;
cout << "a:" << a << "b: " << b << endl;
swap2(a,b);
cout << "a:" << a << "b: " << b << endl;
cout<<"Hello World";
return 0;
}
STL 已经提供了 swap 方法,为了便于区别这里使用了 swap2 作为名称
模板函数参数列表
- 模板参数列表
template<typename T> - 函数参数列表
void swap2(T &a, T &b)
这里 T 表示参数类型占位符,当在调用 swap 时通过为 T 指定一个类型来确定调用函数时参数的类型。根据类型参数确定参数类型是发生编译期间,并不是发生在运行时。
函数参数是发生编译时还是运行时,这个要具体情况具体分析,根其上下文有关,有关这些暂时还是不算了解,随后搜集资料再来进一步解释吧。
模板参数的作用域
template<typename T>//作用域开始
void swap2(T &a, T &b)
{
T temp{a};
a = b;
b = a;
}//作用域结束
模板实例化
- 函数模板提供一套图纸来用于生成函数
- 从模板中生成一个函数定义的行为被称为模板实例化 一个从函数模板生成的函数定义是一个实例化的函数
函数模板调用
在调用时,程序可以使用实例化名来作为函数名来调用实例化的函数,如下
swap2<int>(a,b);
也可以省略<int> 让编译器根据传入参数来推断其类型,
void swap2<int>(int& a,int& b)
void swap2<string>(string& a,string& b)
注意在实例化函数多了
<string>和<int>这样做是便于区分这两个函数,是两个不同的函数
char c_a = 'a';
char c_b = 'b';
char const* a_ptr = &c_a;
char const* b_ptr = &c_b;
cout << "char a:" << *a_ptr << " char b: " << *b_ptr << endl;
swap2<char const*>(a_ptr,b_ptr);
cout << "char a:" << *a_ptr << " char b: " << *b_ptr << endl;
- 对于上面相同类型调用模板只会实例化一次,例如
支持多个参数
template<typename U,typename V>
U find(U first, U second, V const &t)
typename 和 class
在模板定义时,typename 和 class 作为类型参数的类型,并没有什么区别,可以通用。但是这并不表明在其他位置他们之间是通用的,在 swap2 我们选择 typename 来定义是因为这里 swap 接收的参数有 class 和 non-class 的类型