C++泛型编程:模板

泛型编程是编写与类型无关的代码,而模板是泛型编程的基础

在学习C/C++过程中, 我们可能常用几种函数, 由于传入的参数不同, 需要进行不同的函数的编写, 在C语言中需要根据参数和功能的不同重新编写新的函数, 而在C++中有函数重载这样的机制,一定程度上解决了问题,但是

  1. 重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错

如果C++有一个像生活中存在的模具一样的东西, 我们往里面填入什么材料(类型), 就会生成相应材料的东西(函数/类)就会很方便了

模板分为函数模板和类模板

模板本身不是函数, 而是用于生成特定函数的模具,在编译器编译阶段, 编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用int类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码,对于其他类型也是如此

1.函数模板

函数模板使用格式:

   /<>内可以是class T ,或者是typename T, T也可以换成其他名字,不过最好有明确的意义
    template
    返回值类型 函数名(参数列表...)
    {
    	something...
    }

模板参数的显示实例化和隐式实例化

先看一个简单地例子:
实现一个add函数, 功能是返回传入参数的和

template
T add(T a, T b)
{
	return a + b;
}
int main()
{
	int a = 10;
	int b = 20;
	cout << add(a, b) << endl;
}

根据模板的定义, 上面的add这个函数其实是不确定的,编译器会根据参数的不同来推测出该生成什么样的代码,显然编译器会推导为:

int add(int a, int b)
{
	return a + b;
}

上面的代码隐式示例化了函数模板,

在函数模板中,编译器是不做隐式类型转换的,有些情况下用户需要自己显示地指定传入的参数类型
这样的做法叫做显式实例化:

template
T add(T a, T b)
{
	return a + b;
}
int main()
{
	int a = 10;
	double b = 20;
	//a是int类型, 而b是double类型, 模板只提供了一个T,编译器无从得知该吧a和b推成
	//什么类型,所以在这里编译器就会报错
	cout << add(a, b) << endl;
}

此时解决方法也很简单,编译器不知道该怎么推, 我们直接告诉他就好了

template
T add(T a, T b)
{
	return a + b;
}
int main()
{
	int a = 10;
	double b = 20;
	//这里直接把add模板显示实例化为add,告诉编译器T是int类型
	//此时如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
	cout << add(a, b) << endl;
}

模板参数的匹配原则

简单总结起来:
1.有现成的,也是最合适的,就用现成的
2.有现成的,也有需要推导才能更合适的.就用推导出的更合适的
3.没有现成的,只能推导新的

举几个例子:
1.有现成的,也是最合适的,就用现成的

// 专门处理int的加法函数
int Add(int a, int b)
{
	return a + b;
}
// 通用加法函数
template
T Add(T a, T b)
{
	return a + b;
}
int main()
{
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化(现成的)
	Add(1, 2); // 调用编译器特化的Add版本(显示实例化了)
	return 0;
}

2.有现成的,也有需要推导才能更合适的.就用推导出的更合适的

// 专门处理int的加法函数
int Add(int a, int b)
{
	return a + b;
}
// 通用加法函数
template
int Add(T1 a, T2 b)
{
	return a + b;
}
int main()
{
	//int和double类型参数,与int,int类型不匹配,调用的是编译器
	//推导的通用加法函数(编译器推导的更合适)
	Add(1, 2.5);
	return 0;
}

由模板的匹配,引出另一个问题:模板的特化

我们知道, 因为浮点数的存储并不精确, 浮点数不能直接判断是否相等,可是如果我们写了一个用于判断是否相等的函数模板,例如:

template
bool Equal(T a, T b)
{
	return (a == b) ? true : false;
}

如果我们给这个模板传入两个浮点数,肯定是不合适的
所以我们需要利用模板匹配的规则,写一个专门处理浮点数是否相等的函数:

bool Equal(double a, double b)
{
	//如果这两个浮点数的差值的绝对值小于规定的某值,就可判断为近似相等
	if(abs(a - b) < 0.0001)
	{
		return true;
	}
	return false;
}
template
bool Equal(T a, T b)
{
	return (a > b) ? true : false;
}

由于函数模板的匹配规则, 用户传入浮点数时,会调用现成的最为合适的函数,因此解决了浮点数的比较相等问题
这样的处理方式就是模板的特化 : 也就是我们常说的特殊情况特殊对待

类模板

类模板定义格式:

template
class 类模板名
{
	// 类内成员定义
};

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类

你可能感兴趣的:(LearnC++)