第20课 - 函数模板
一.C++中如何交换两个变量的值
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
void swap(float& a, float& b)
{
float t = a;
a = b;
b = t;
}
void swap(char*& a, char*& b)
{
char* t = a;
a = b;
b = t;
}
这三个函数,除了类型不同,函数体代码完全相同。
C++强调代码复用,如何解决这个代码冗余的问题?
二.泛型编程
2.1 不考虑具体数据类型的编程模式
对于Swap函数可以考虑下面泛型的写法
void Swap(T&a, T&b)
{
T t = a;
a = b;
b = t;
}
Swap泛型写法中T不是一个具体的数据类型,而是泛指的任意数据类型。
2.2 C++中的泛型编程->函数模板
2.2.1 提供一种特殊的函数可用不同类型进行调用
2.2.2 看起来和普通函数很相似,区别是类型可被参数化
template <typename T>
void Swap(T&a, T&b)
{
T t = a;
a = b;
b = t;
}
2.2.3 函数模板的语法规则
a.template关键字用于声明开始进行泛型编程
b.typename关键字用于声明泛指类型
2.2.4 函数模板的应用
a.自动类型推导调用
b.具体类型显示调用
int a = 1;
int b = 1;
Swap(a,b); -->自动类型推导:a和b均是int,因此参数T为int
flaot fa = 3;
flaot fb = 4;
Swap<float>(fa, fb);-->显示类型调用:用float替换参数类型T
Source Example 2.2.4:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template <typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main(int argc, char** argv) {
int a = 1;
int b = 0;
Swap (a, b);
printf("a = %d, b = %d\n", a , b );
float c = 1.2;
float d = 1.3;
Swap<float>(c, d);
printf("c = %f, d = %f\n", c , d );
char ca = 'a';
char cb = 'b';
Swap (ca, cb);
printf("ca = %c, cb = %c\n", ca , cb );
return 0;
}
2.2.5 泛型算法:
Source Example 2.2.5(选择排序):
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template <typename T>
void Swap(T& a, T& b)
{
T& t = a;
a = b;
b = t;
}
template <typename T>
void SelectSort(T array[], int length)
{
for (int i = 0; i < length; i++)
{
T min = array[i];
int index = i;
for (int j = i + 1; j < length; j++)
{
if (array[j] < min)
{
min = array[j];
index = j;
}
}
Swap(array[i], array[index]);
}
}
int main(int argc, char** argv) {
int array[] = {3,2,5,4,5};
SelectSort<int>(array, 5);
for (int i = 0; i < 5; i++)
{
printf("array[%d] = %d\n", i ,array[i]);
}
return 0;
}
输出结果如下:
2.2.6 函数模板的深入理解
a.编译器并不是把函数模板处理成能够处理任意类型的函数
b.编译器从函数模板通过具体类型产生不同的函数
c.编译器会对函数模板进行两次编译
在声明的地方对模板代码本省进行编译,会进行语法检查
在调用的地方对参数替换后的代码进行编译
d. 函数模版本身不允许隐士类型转换,自动推导类型时,必须严格匹配。显示类型指定时,可以进行隐式类型转换。
三.函数重载遇上函数模板
3.1 函数模板可以像普通函数一样被重载
3.1.1 C++编译器优先考虑普通函数
3.1.2 如果函数模板可以产生一个更好的匹配,那么选择模板
3.1.3 可以通过空模板实参列表的语法限定编译器只通过模板匹配
Source Example 3.1:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int Max(int a, int b)
{
printf("nomanl!\n");
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
printf("template!\n");
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b,T c)
{
/* A,B是未知的泛型,会调用函数模板 */
return Max(Max(a,b), c);
}
int main(int argc, char** argv) {
int a = 1;
int b = 2;
/* 优先调用普通的Max函数 */
printf("%d\n", Max(a,b));
/* 通过空模板实参列表限定编译器调用模板函数 */
printf("%d\n", Max<>(a,b));
float fa = 3;
float fb = 4;
/* 由于是浮点型,会优先匹配函数模板 */
printf("%f\n", Max(fa,fb));
char ca = 5;
char cb = 6;
char cc = 7;
printf("%d\n", (int)Max(ca, cb, cc));
/* 调用普通函数,因为模板不允许进行类型转换,普通函数可以 */
Max('a', 100);
return 0;
}
输出结果如下:
3.2 注意事项
3.2.1 函数模板不允许自动类型转化
3.2.2 普通函数能够进行自动类型转换
3.3 函数模板可以定义任意多个不同的类型参数
但是问题多个类型参数的模板可以支持自动类型推导吗?
当声明的类型参数为返回值类型时,无法进行自动类型推导。
Source Example 3.3
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template<typename T1,typename T2,typename RT>
RT Add(T1 a,T2 b)
{
return static_cast<RT>(a + b);
}
int main(int argc, char** argv) {
/* 显示调用,没有问题 */
printf("%lf", Add<char, float, double>('a', 100));
/* 推导不出来返回值类型,会报错 */
printf("%lf", Add('a', 100));
return 0;
}
3.3.1 不完美的解决方案
将返回类型参数声明放到第一个参数位置,调用时只需要显示声明返回类型参数即可
Source Example 3.3.1
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
/* 返回类型要放在第一个 */
template<typename RT,typename T1,typename T2>
RT Add(T1 a,T2 b)
{
return static_cast<RT>(a + b);
}
int main(int argc, char** argv) {
/* 显示调用,没有问题 */
printf("%lf\n", Add<double, char, float>('a', 100));
/* 提供返回类型 */
printf("%lf", Add<double>('a', 100.0f));
return 0;
}
四.小结
4.1 函数模板其实是一个具有相同行为的函数家族
4.2 函数模板可以根据类型实参对函数进行推导调用
4.3 函数模板可以显示的指定类型参数
4.4 函数模板可以被重载