泛型编程与模板
泛型编程
泛型编程(Generic Programming)是一种语言机制,通过它可以实现一个标准的容器库
像类一样,泛型也是一种抽象数据类型,但是泛型不属于面向对象,它是面向对象的补充和发展
- 泛型编程在C++中的主要实现为函数模板和模板类
template
函数模板
- 把处理不同类型的公共逻辑抽象成函数,就得到了函数模板
普通函数模板
假设需要定义一个函数,用来交换两个变量的值,但是输入的变量暂时不确定是什么类型,那我们需要进行如下定义
void swap(int &a, int &b) {
int tmp{a};
a = b;
b = tmp;
}
void swap(float &a, float &b) {
/* Code */
}
void swap(double &a, double &b) {
/* Code */
}
//...
由于输入的变量类型并不确定,需要针对不同的变量类型对函数进行重载,将会使代码变得冗长
如果我们使用模板,就可以方便的实现
// template<class T>
template<typename T>
void swap(T &a, T &b) {
T tmp{a};
a = b;
b = tmp;
}
template<typename T>
将T定义为任意数据类型,在模板函数调用的时候才会被确定- 这个数据类型由
typename
关键字引导,也可以使用class
关键字来引导 - 如果使用到模板的嵌套的话,就必须使用
typename
- 为了避免一些阅读上的歧义,更推荐
typename
- 这个数据类型由
成员函数模板
成员函数模板的实现与普通函数模板没有什么区别
class Printer {
public:
template<typename T>
void print(const T& t) {
cout << t <<endl;
}
};
需要注意的是
- 成员函数模板不能是
virtual
虚函数 - 成员函数模板不能有缺省参数
函数模板重载
函数模板之间,函数模板与普通函数之间都可以重载。编译器会根据调用时提供的函数参数,调用能够处理这一类型的最特殊的版本
在特殊性上一般按照如下顺序考虑
- 普通函数
- 特殊模板(限定了
T
的形式的,指针、引用、容器等) - 普通模板(对
T
没有任何限制的)
template<typename T>
void func(T &t) { //通用模板函数
cout << "In generic version template " << t << endl;
}
template<typename T>
void func(T *t) { //指针版本
cout << "In pointer version template "<< *t << endl;
}
void func(string *s) { //普通函数
cout << "In normal function " << *s << endl;
}
int main() {
int i = 10;
func(i); //调用通用版本,其他函数或者无法实例化或者不匹配
func(&i); //调用指针版本,编译器选择最特殊的版本
string s = "abc";
func(&s); //调用普通函数,编译器选择最特化的版本
func<>(&s); //调用指针版本,通过<>告诉编译器需要用 template
return 0;
}
模板函数的特化
有时通用的函数模板不能解决个别类型的问题,我们可以使用函数模板的特化进行特殊处理
- 函数模板特化必须将所有的模板参数全部指定
例如
template<>
void func(int i) {
// code
}
tip
虽然有模板特化的处理方法,但是更多时候更推荐使用重载来解决这样的问题