一、泛型编程
泛型编程是什么意思呢?我们通过下面的例子来具体了解:
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { double temp = left; left = right; right = temp; } int main() { int a = 1, b = 2; Swap(a, b); double c=1.33,d=2.33; Swap(a,b) }
就拿交换函数来说,当我们交换不同类型的变量的值,那就需要不停的写交换函数的重载,这样代码复用率就较低,那我们能不能创造一个模板呢??
一个Swap的模板,但是我可以用不同的类型去实现这个模板,继而试用它。
如果在 C++ 中,也能够存在这样一个 模具 ,通过给这个模具中 填充不同材料 ( 类型 ) ,来 获得不同材料的铸件 ( 即生成具体类型的代码)。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
二、模板(初阶)
模板分为:函数模板和类模板
1.函数模板
1.单参数类型
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定 类型版本。
就拿Swap来说: typename 是 用来定义模板参数 关键字,T是类型(也可以用class,(class T))
template<typename T> void Swap(T& left, T& right) { T temp = left; left = right; right = temp; } int main() { int a = 1, b = 2; Swap(a, b); int x = 1, y = 2; Swap(x, y); double m = 1.1, n = 2.2; Swap(m, n); char p = 'a', q = 'b'; Swap(p, q); Swap(m,a);//不同类型 }
那么,具体是怎样实现的呢?
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
编译器通过类型推演,将函数模板进行实例化,对应的T就会替换成具体的类型,模板实例化是用几个实例化几个,不是所有不同类型都提前模板实例化。
1.当变量类型相同,但是变量不同,调用Swap();模板实例化只会实例化一个,因为虽然变量不同,但类型相同,模板实例化就是将T换成具体的类型。
2.当Swap(m,a),变量是不同类型时,会发生什么??
因为在推演void Swap(T& left, T& right);时,T的类型不明确,就会发生错误(推演报错),直接报错
但如果不用模板,我们自己这样:
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } int main() { int a=2; double b=2.22; Swap(a,b); }
可能有人就会想:在Swap(a,b)中,会不会a和b发生饮食类型转化呢?较小的类型转化成较大的类型。
当然不会:隐式类型转化只有在 赋值:b=3;(产生临时变量);函数传参的时候(产生临时变量),才会发生隐式类型转化。
函数形参是引用,当类型是引用时,我们就要小心:是否会发生权限放大?当b传值时,中间的临时变量具有常性(只读),而形参是可读可写,权限就会放大,也是不可以通过的,除非加了const,但是加了const就无法交换了,所以这样还是行不通的!
自动推演实例化和显式实例化:
template<class T> T Add(const T& left, const T& right) { return left + right; } int main() { int a1 = 10, a2 = 20; double d1 = 10.1, d2 = 20.2; // 自动推演实例化 cout << Add(a1, a2) << endl; cout << Add(d1, d2) << endl; cout << Add((double)a1, d2) << endl; //强制类型转化也是产生临时变量,不是改变a1 cout << Add(a1, (int)d2) << endl; // 显示实例化 cout << Add<double>(a1, d2) << endl;//隐式类型转化 cout << Add<int>(a1, d2) << endl; return 0; }
在自动推演实例化中,必须强转,不然还是和之前问题一样,该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错。 (推演报错)
不强转情况:显示实例化,:在函数名后的<>中指定模板参数的实际类型(我让你怎么来你就怎么来!)
在函数名后加入了指定模板参数后,就会在实例化时,T直接是指定的类型,这样就会发生隐式类型转换。
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
Add(a1, d1);
此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
2.多参数类型
template
template<class t1,class t2> t1 Add(const t1& left, const t2& right) { return left + right; } int main() { int a = 1, b = 2; double m = 2.22, n = 3.33; cout << Add(a, b) << endl; cout << Add(m, n) << endl; cout << Add(a, m) << endl; cout << Add(n, b) << endl; }
此时,当Add(不同类型时),就不会发生推演错误,你是什么类型就会推演成什么模板函数。