(仅为个人学习笔记)
函数模板是什么
通过泛型来定义函数 ,通过将类型作为参数传递给模板,使得编译器生成该类型的函数。模板不生成任何函数,只是告诉编译器如何定义函数。
template<typename T>
void Swap(T &a, T &b)
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
模板需要放在头文件中
模板的重载
模板可以进行重载。但是与普通函数重载一样,需要特征标不同
如 (T &, T&)
和 (T[] , T[], int)
template<typename T>
void Swap(T &a, T &b)
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
template<typename T>
void Swap(T a[] , T b[], int)
{
T tmp;
for(int i = 0;i < n;i++)
{
tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
局限性
(1)就如上面两个重载。如果T 为 int ,那么对于第二个模板 ,a 和 b 都是数组,那么a = b;则不成立。
(2)依然是那个狡猾函数模板。如果传入的参数不同类型呢?
比如
int a = 3;
double b = 12.2;
Swap<double>(a, b);
报错。第二个参数类型时double&,无法指向一个 int。
但是并非都会报错。
template<typename T>
T Add(T a, T b)
{
return a + b;
}
int a = 9;
double b = 13.3;
cout<<Add<double>(a + b);
这是成立的。第一个参数虽然是 int, 但是可以强制转换为double。
具体化(特化)
显式具体化
为模板提供一个具体化的函数定义,即模板的显式具体化,其中包含所需的代码。
当编译器找到与函数调用匹配的具体化定义时,直接使用这个具体化的版本,而不是调用模板。
代码实例参考c++primerplus - 286
写法:
template<> void Swap<job>(job &, job &);
依然是上面那个模板,定义交换函数。如果是传入int类型的参数,那么继续使用模板的交换方法。如果是job这个自定义的,会使用这个特化的版本。
隐式实例化
在这个交换的模板中,如果我直接传入参数去调用模板。
比如 int a = 1, b = 2;Swap(a, b);
那么编译器会生成一个Swap()的实例,实例使用int类型,这种便是隐式实例化。
显式实例化
与隐式不同,显式则为提前声明,直接命令编译器创建特定的实例。
语法为 template void Swap<int>(int, int)
Tips 这里语法注意与显式具体化分开,具体化在template后有一对<>, 而实例化没有。
而且理解上,实例化与具体化的区别是:
实例化:编译器看好了,我要这个类型参数版本的函数的实例
具体化:编译器看好了,你别管,不需要模板来为我生成函数,而是需要我自己专门显示定义好的。
相同之处是
他们都是具体类型的函数定义,而不是模板,那种通用的描述。
调用哪个函数
函数重载,函数模板,函数模板重载。编译器该如何选择?
- 选择名称相同的函数和模板函数,作为候选列表。(名称)
- 确定可行的函数列表。即参数列表需要正确。(参数)
- 使用最佳匹配的
常规函数优先于模板,而特化版优先于非特化版
参数的匹配,是可以进行类型转换的。但是不能转换为指针。且非const的指针和引用 优先于 const 的指针和引用参数匹配。
void func(job &);
void func(const job &);
以上两种,编译器优先使用 1.
void func(job);
void func(const job);
由于是指针和引用的限定,对于这两种,优先级同。会产生二义性。
decltype
局限性
template<class T1, class T2>
?type? gt(T1 x, T2 y)
{
retrun x + y;
}
这个type怎么办?x , y还未声明, 不在作用域内,编译器看不到。
c++11 有了auto ,解决了这个问题。auto 是一个占位符,表示后置返回类型提供的类型。
template<class T1, class T2>
auto gt(T1 x, T2 y) -> decltype(x+y)
{
retrun x + y;
}
现在decltype在参数声明之后,编译器可以看到了