目录
一,模板函数、模板类
1,模板函数
像max这种函数,对于参数类型,计算逻辑是一样的,只是入参和返回值类型不同,就可以写成模板函数。
示例:
#include<iostream>
using namespace std;
template<typename T>
T maxmax(T a, T b)
{
return (a > b ? a : b) + 1;
}
int main()
{
cout << maxmax(1, 2) << endl;
cout << maxmax(1.3, 1.5) << endl;
cout << maxmax("as", "zx");
return 0;
}
输出:
3
2.5
s
但是,不能这么调用:maxmax(1.3,1),这样写会报错,有重载歧义。
2,模板类
和模板函数语法差不多。
示例:
#include<iostream>
using namespace std;
template<typename T>
class GetMax
{
public:
T a;
T b;
GetMax(T a, T b)
{
this->a, this->b = b;
}
T getMax()
{
return a > b ? a : b;
}
};
int main()
{
int a = 1;
cout << GetMax<double>(a, 2.3).getMax();
return 0;
}
有个很重要的区别就是,模板类在调用的时候必须显示的指明类型参数,
所以这里就像普通函数一样,会隐式地把int类型的数转换为double的数。
(1)可继承性
模板类可以继承模板类,模板类可以继承非模板类,
非模板类可以继承模板类,非模板类可以继承非模板类。
(2)友元
友元的使用,对于模板类和非模板类是一样的。
(3)静态成员
对于模板类中的静态成员,每个模板具体化都拥有独立的静态数据域拷贝。
二,模板参数
1,多类型参数
要实现有不同数据类型的入参的函数,就需要多个模板参数。
示例:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
T1 maxmax(T1 a, T2 b)
{
return (a > b ? a : b) + 1;
}
int main()
{
cout << maxmax(1, 2) << endl;
cout << maxmax(1.3, 1.5) << endl;
cout << maxmax(1.3, 1) << endl;
cout << maxmax("as", "zx");
return 0;
}
输出:
3
2.5
2.3
s
示例:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
class GetMax
{
public:
T1 a;
T2 b;
GetMax(T1 a, T2 b)
{
this->a, this->b = b;
}
T1 getMax()
{
return a > b ? a : b;
}
};
int main()
{
int a = 1;
cout << GetMax<int, double>(a, 3.3).getMax() << endl;
cout << GetMax<double,int>(a, 3.3).getMax();
return 0;
}
输出:
3
3
2,默认类型
template<typename T1 = double>
void f(T1 a)
{
cout << a;
}
int main()
{
int a = 1;
f(a);
f(1.5);
return 0;
}
这个例子太简单了,看起来提供默认类型并没有什么用。
模板类定义时,类型参数也可以指定默认类型。
template<typename T=int>
class MyClass
{
public:
T x;
};
int main() {
MyClass<> cs;
return 0;
}
3,模板函数的类型推导
我在用泛型编程写二维vector的排序模板时,写出这样一个代码:
//vector的字典序比较,v1<v2是true,v1>=v2是false
template<typename T>
bool cmp(vector<T>&v1,vector<T>&v2)
{
for(int i=0;i<v1.size()&&i<v2.size();i++)
{
if(v1[i]!=v2[i])return v1[i]<v2[i];
}
return v1.size()<v2.size();
}
//vector的字典序排序
template<typename T>
void sortVector(vector<vector<T>>&v)
{
sort(v.begin(),v.end(),cmp);
}
编译结果:
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3802) : 参见“std::sort”的声明
1>c:\users\z00454773\documents\visual studio 2010\projects\20191228.cpp\20191228.cpp\csimsgeek.cpp(273): error C2780: “void std::sort(_RanIt,_RanIt)”: 应输入 2 个参数,却提供了 3 个
这就很奇怪了,一般在sort里面用自定义排序函数,就是加函数指针啊!
很快,我就想到了,这里的cmp是模板函数,它是需要根据类型参数来实例化的。
也就是说,模板函数直接调用是可以自动推导类型的,但是如果要用做函数指针的话,需要传入类型参数才能实例化。
正确代码:
//vector的字典序排序
template<typename T>
void sortVector(vector<vector<T>>&v)
{
sort(v.begin(),v.end(),cmp<T>);
}
4,类参数
类型参数除了可以是任意类型,也可以限定只能是某种class类型。
template <class A>
class PickX
{
public:
int f(A p)
{
return p.x;
}
};
5,非类型参数
在定义模板类时,参数列表除了类型参数还可以有变量参数。
示例:
#include<iostream>
using namespace std;
template<typename T,int len>
class Num
{
public:
Num(T a)
{
this->num[0] = a;
}
T get()
{
return num[0];
}
private:
T num[len];
};
int main()
{
cout << Num<int, 1>(2.3).get();
return 0;
}
输出:
2
注意,创建模板类实例时,变量参数只能传常量,不能传变量,即使模板类中没有使用这个参数也是这样。
三,具体化、实例化、特化
1,具体化
模板代码本身是不能编译的,要指定类型生成具体代码之后才能编译。
具体化分为2种,一种是实例化,内容和模板一致,一种是特化,内容往往和模板不一致。
2,隐式实例化
使用时根据类型推导进行隐式实例化
#include<iostream>
using namespace std;
template<typename T>
T maxmax(T a, T b)
{
return (a > b ? a : b) + 1;
}
int main()
{
cout << maxmax(1, 2) << endl;
cout << maxmax(1.3, 1.5) << endl;
cout << maxmax("as", "zx");
return 0;
}
这里隐式实例化为3种类型,整数,浮点数,字符串。
3,显式实例化
#include<iostream>
using namespace std;
template<typename T>
T maxmax(T a, T b)
{
return (a > b ? a : b) + 1;
}
template int maxmax(int a, int b); //
int main()
{
cout << maxmax(1, 2) << endl;
cout << maxmax(1.3, 1.5) << endl;
cout << maxmax("as", "zx");
return 0;
}
4,特化
特化指的是和模板签名一致,但并非实例化的代码。
#include<iostream>
using namespace std;
template<typename T>
T maxmax(T a, T b)
{
return (a > b ? a : b) + 1;
}
int maxmax(int a, int b)
{
return 0;
}
int main()
{
cout << maxmax(1, 2) << endl;
cout << maxmax(1.3, 1.5) << endl;
cout << maxmax("as", "zx");
return 0;
}
5,偏特化
偏特化指的是多类型参数的情况下,部分类型进行特化,部分类型仍然保持模板类型。
偏特化代码可以理解成介于模板代码和特化代码的中间状态,也可以理解成是特化的一种。
6,经典应用
四,函数重载 待更新
1,模板间重载
template<typename T1>
void f(T1 a)
{
cout << 111;
}
template<typename T1, typename T2>
void f(T1 a, T2 b)
{
cout << 222;
}
template<typename T1, typename T2>
void f(T1 a, T2 b, int c)
{
cout << 333;
}
template<typename T1, typename T2>
void f(T1 a, T2 b, double c)
{
cout << 4444;
}
int main()
{
int a = 1, b = 2;
f(a);
f(a, b);
f(a, b, 1);
f(a, b, 1.5);
return 0;
}
输出:1112223334444
2,普通函数和模板函数间重载
待更新
五,仿函数与模板编程
本章讨论,模板函数或者模板类以函数指针为形参,以仿函数为实参的情形。
1,模板函数
示例:
#include<iostream>
using namespace std;
int fun()
{
return 2;
}
class FUN
{
public:
int operator()()
{
return 3;
}
};
class FUN2
{
public:
int fun2()
{
return 3;
}
};
template <class A>
int f(A p)
{
return p();
}
int main()
{
cout << f(fun) << " " << f(FUN()) << " ";
//cout << f(FUN2()); 错误
//cout << f(FUN2().fun2()); 错误
return 0;
}
输出:
2 3
可以看出,当我们这样实现模板函数时,既可以传入普通函数指针,也可以传入仿函数类对象。
如果是普通类,无论传入对象还是成员函数都是错的。
2,模板类
如果是模板类,就只能传入仿函数类对象,不能传入普通函数。
示例:
#include<iostream>
using namespace std;
class FUN
{
public:
int operator()()
{
return 3;
}
};
template <class A>
class classf
{
public:
int f(A p)
{
return p();
}
};
int main()
{
cout << classf<FUN>().f(FUN());
return 0;
}
六,通透比较器
普通的比较函数:
其中T的默认值是void就表明不能