C++模板编程与泛型编程之函数模板
重载
- 函数(函数模板)名字相同,但是参数数量或者参数类型上不同。
template<typename T>
void myfunc(T tmpvalue)
{
cout << "myfunc(T tmpvalue)执行了" << endl;
}
myfunc(12); // 输出 myfunc(T tmpvalue)执行了
char* p = nullptr;
myfunc(p); // 输出 myfunc(T tmpvalue)执行了
- 第一次调用,自动推断为 int 类型,第二次自动推断为 char * 类型。
下面进行模板重载
template<typename T>
void myfunc(T tmpvalue)
{
cout << "myfunc(T tmpvalue)执行了" << endl;
}
template<typename T>
void myfunc(T* tmpvalue)
{
cout << "myfunc(T* tmpvalue)执行了" << endl;
}
myfunc(12);
char* p = nullptr;
myfunc(p);
- 输出为 :
myfunc(T tmpvalue)执行了
myfunc(T* tmpvalue)执行了
- 说明char * p通过重载版本的模板函数进行了实例化。
- 函数模板和函数也可以同时存在,此时可以把函数看成是一种重载
- 当普通函数和函数模板都比较合适的时候,编译器会优先选择普通函数来执行。
template<typename T>
void myfunc(T tmpvalue)
{
cout << "myfunc(T tmpvalue)执行了" << endl;
}
template<typename T>
void myfunc(T* tmpvalue)
{
cout << "myfunc(T* tmpvalue)执行了" << endl;
}
void myfunc(int tmpvalue)
{
cout << "myfunc(int tmpvalue)执行了" << endl;
}
myfunc(12);
char* p = nullptr;
myfunc(p);
myfunc(12.1);*/
- 输出为:
myfunc(int tmpvalue)执行了
myfunc(T* tmpvalue)执行了
myfunc(T tmpvalue)执行了
- 如果选择最合适(最特殊)的函数模板/函数,编译器内部有比较复杂的排序规则,规则也在不断更新
特化
- 泛化(泛化版本):
- 大众化的,常规的。常规情况下,写的函数模板都是泛化的函数模板
- 特化(特化版本):
- 往往代表着从泛化版本中抽出来的一组子集。
-
泛化版本
template <typename T,typename U>
void tfunc(T& tmprv, U& tmprv2)
{
cout << "tfunc泛化版本" << endl
cout << tmprv << endl;
cout << tmprv2 << endl;
}
const char* p = "I Love China!";
int i = 12;
tfunc(p, i);
- 其中
T = const char *;
U = int
tmprv = const char * &
tmprv2 = int &
- 全特化版本
- 全特化:就是把tfunc这个泛化版本中的所有模板参数都用具体的类型来代替构成的一个特殊的版本(全特化版本);
- 全特化实际上等价于实例化一个函数模板,并不等价于一个函数重载。
- void tfunc<int ,double>(int& tmprv, double& tmprv2){......} 全特化的样子
- void tfunc(int& tmprv, double& tmprv2) { ...... } 重载函数的样子
- 注意全特化前面有template<>,而重载没有。
template <> //全特化<>里面为空
void tfunc<int ,double>(int& tmprv, double& tmprv2)//<int, double>可以省略,因为根据实参可以完全推导出T和U的类型。
//void tfunc(int& tmprv, double& tmprv2)
{
cout << "tfunc<int,double>特化版本" << endl;
cout << tmprv << endl;
cout << tmprv2 << endl;
}
int k = 12;
double db = 15.8;
tfunc(k, db);
- 下面加入重载函数,如果实参匹配重载函数,则优先调用重载函数
//全特化
template <>
void tfunc(int& tmprv, double& tmprv2)
{
cout << "tfunc<int,double>特化版本" << endl;
cout << tmprv << endl;
cout << tmprv2 << endl;
}
//重载函数
void tfunc(int& tmprv, double& tmprv2)
{
cout << "tfunc普通函数" << endl;
}
int k = 12;
double db = 15.8;
func(k, db);
- 编译器考虑的顺序:
- 优先选择普通函数,然后才会考虑函数模板的特化版本,最后才会考虑函数模板的泛化版本。
-
偏特化(局部特化)
- 从两方面来说:
- 一个是模板参数数量上的偏特化
- 一个是模板参数范围上的偏特化
- 从两方面来说:
- 模板参数数量上的偏特化:
- 比如针对tfunc函数模板,第一个模板参数类型为double,第二个模板参数不特化;
//从模板参数数量上的偏特化
template <typename U>
void tfunc<double, U>(double& tmprv, U& tmprv2)
{
//.......
}
- 编译时报错
- 实际上,从模板参数数量上来讲,函数模板不能偏特化。否则会导致编译错误
- 模板参数范围上的偏特化:
- 范围上:
- int->const int,T -> T*,T -> T&,T -> T&&。
- 针对T类型,从类型范围上都变小了。
- 实际上,对于函数模板来讲,也不存在模板参数范围上的偏特化。因为这种所谓模板参数范围上的偏特化,实际上是函数模板的重载。
- 范围上:
template<typename T>
void myfunc(T tmpvalue)
{
cout << "myfunc(T tmpvalue)执行了" << endl;
}
template <typename T, typename U>
void tfunc(const T& tmprv, U& tmprv2)
{
cout << "tfunc(const T& tmprv, U& tmprv2)重载版本" << endl;
}
const int k2 = 12;
tfunc(k2, db);
- 输出:
tfunc(const T& tmprv, U& tmprv2)重载版本
- 函数调用,选择范围更小,更匹配,更“精确”的调用。
- 通过重载实现模板参数数量上的偏特化
//泛化版本
template <typename T,typename U>
void tfunc(T& tmprv, U& tmprv2)
{
cout << "tfunc泛化版本" << endl
cout << tmprv << endl;
cout << tmprv2 << endl;
}
//重载
template <typename U>
void tfunc(double& tmprv, U& tmprv2)
{
cout << "类似于tfunc<double, U>偏特化的tfunc重载版本" << endl;
cout << tmprv << endl;
cout << tmprv2 << endl;
}
double j = 18.5;
tfunc(j, i);
- 输出:
类似于tfunc<double, U>偏特化的tfunc重载版本
- 类模板时,对于类模板,还是存在模板参数范围上的偏特化以及数量上的偏特化。