文章目录
元编程&泛型编程(Meta-Programming&Generic Programming)
概念
编程:写一个程序去处理数据
元编程:写一个程序去处理程序(写一个程序去处理其他程序)
泛型编程:写一个程序,可以处理多种数据类型。
实现
元编程:C++使用模板(template)实现元编程。即由编译器在编译期根据模板生成程序。
模板可以是类、函数。
泛型编程:即编译时多态,是由元编程实现。
模板(Template)
为什么要使用模板?
如果我们要处理两个整数,两个双精度浮点数,两个单精度浮点数的最大值。
- C语言中,我们需要使用三个函数,且这三个函数的名称不能相同。
- C++中用重载函数,也需要写三个函数,但函数名称可以相同。
因此引入模板,一个函数可以处理多个不同的类型却功能相同的数据。
函数模板
template<typename T> returnType functionName(T paral1,...){
//function Body
}
template<typename T>
模板前缀typename
为需要识别的类型名称returnType
返回值类型functionName
函数名T
类型参数(type parameter)T paral
类型为T的参数。
T可以出现在三个地方:
- 返回值
- 参数
- 函数中的局部变量。
要求:T类型的参数在模板形参中最少出现一次。
如何声明类型参数
- <typename T>:描述性强,较好
- <class T>:易与类混淆
处理多个类型的参数
代码实例:
template<typename T,typename S>
auto add(T x1,S x2){
return (x1+x2);
}
}
auto作为函数返回类型是c++14中才有的。
使用函数模板
使用上方模板,有:
auto y = add(1,2.0);
编码规范
用于表示模板类型的名字应该使用一个大写字母(T,S)。
使用案例
#include <iostream>
template <typename T>
T add(T x,T y){
return (x+y);
}
template <typename T,typename Q>
auto add(T x,Q y){
return (x+y);
}
int main(){
std::cout << add(3,2)<<std::endl;
std::cout << add(3.2,2)<<std::endl;
}
函数模板的实例化
- 函数模板只是蓝图,本身不是类型,函数。
- 编译器扫描代码,遇到模板定义时,并不会产生代码
- 确定模板实参后,编译器才会生成实际函数模板
两种实例化方式
1. 显式实例化
模板
template <typename T>
void f(T s){
std::cout<<s<<'\n'
}
实例化
template void f<doublr>(double);
尖括号中的double确定了模板中的T。
圆括号中的double为传入参数的类型。
其中尖括号中的模板实参可以由形参类型推导,也可以有如下两种实例化方法。
template void f<>(char);
template void f(int);
2. 隐式实例化
编译器查看函数调用,推断模板实参,实现隐式实例化。
f<double>(1); //调用f<double>(double)
f<>('a');//调用f<char>(char)
f(7);//调用f<int>(int)
void (*ptr)(std::string) = f;
(*ptr)
为函数指针(std::string)
为函数参数- 指向
f
,f
实例化为f<string>(string)
实例函数 / 实例类
由模板函数实例化得到的函数叫做“实例函数”。
由类模板实例化得到的类叫做“实例类”。
名字命名空间冲突问题
如果引入的头文件中与某个定义的模板起冲突,则会报错。
不要轻易定义和标准库同名的函数模板。
排序实例泛型化
对于不确定大小的数组容器
函数
template <int N> void selectionSort(std::array<double,N> &list){
constexpr int size = N;
//排序算法
}
调用
std::array x{数据};
selectionSort(x);
强调
使用模板,不要将模板的声明和实现放在不同文件中。