1. 函数模板
函数模板是通用的函数描述,使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将特定类型的参数传递给模板,可使编译器生成该类型的函数。
模板的写法:
template<typename AnyType> // typename可以用class替换,兼容旧的代码
void Swap(AnyType &a, AnyType &b)
{
AnyType Temp;
temp = a;
a = b;
b = Temp;
}
将同一种算法应用于不同的类型,可用使用模板。
模板并不创建任何函数,只是告诉编译器如何定义函数。
要使用模板的一个特定形式的函数,只需要在程序中调用Swap函数既可。编译器会检查使用的参数类型,并生成相应的函数。
如下所示的代码中使用了函数模板:
#include <iostream>
template <typename T> // class T
void Swap(T &a, T &b);
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i, j = "<<i<<","<<j<<".\n";
cout << "Using complier-generated int swapper:\n";
Swap(i, j);
cout << "Now, i, j = " << i <<", " << j << ".\n";
double x = 24.5;
double y = 81.7;
cout << "x, y = "<<x<<","<<y<<".\n";
cout << "Using complier-generated double swapper:\n";
Swap(x, y);
cout << "Now, x, y = " << x <<", " << y << ".\n";
// cin.get();
return 0;
}
template<typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}

模板Swap定义了函数生成的方式,传入int类型的参数,编译器就会生成int类型的版本。
2. 重载的模板
可以像重载普通函数那样重载函数模板。仍然是需要特征标不同。如下代码重载了两种不同的Swap函数模板,在使用时传入不同的参数。
#include <iostream>
template <typename T>
void Swap(T &a, T &b);
template <typename T>
void Swap(T *a, T *b, int n);
void Show(int a[]);
const int Lim = 8;
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i, j = "<<i<<","<<j<<".\n";
cout << "Using complier-generated int swapper:\n";
Swap(i, j);
cout << "Now, i, j = " << i <<", " << j << ".\n";
int d1[Lim] = {0, 7, 0, 4, 1, 7, 7, 6};
int d2[Lim] = {0, 7, 2, 0, 1, 9, 6, 9};
cout <<"Original arrays:\n";
Show(d1);
Show(d2);
Swap(d1, d2, Lim);
cout <<"Swapped arrays:\n";
Show(d1);
Show(d2);
// cin.get();
return 0;
}
template<typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template<typename T>
void Swap(T a[], T b[], int n)
{
T temp;
for(int i = 0;i < n;i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
void Show(int a[])
{
using namespace std;
cout << a[0] << a[1] <<"/";
cout << a[2] << a[3] <<"/";
for(int i = 4;i<Lim;i++)
{
cout << a[i];
}
cout << endl;
}

但函数模板具有局限性,其预设了数据类型可以执行的操作。比如对于上面的Swap模板,如果T为数组类型,则a = b就不成立。
对于此种问题,一种方法是可以重载运算符,另外一种就是为特定数据类型提供具体化的模板定义,即显示具体化。
3. 显式具体化
对于给定的函数名,可以有非模板函数,模板函数和显式具体化模板函数,以及它们的重载版本存在。
优先级非模板函数 > 显式具体化模板函数 > 常规模板函数
显示具体化模板的定义方式:
template <> void Swap<job>(job &, job &);
该定义为job类型定义了一种新的模板。
如下的代码使用了显式具体化的模板,定义了对于job结构体的Swap函数模板:
#include <iostream>
template<typename T>
void Swap(T &a, T &b);
struct job
{
char name[40];
double salary;
int floor;
};
template <> void Swap<job>(job &j1, job &j2);
void Show(job &j);
int main()
{
using namespace std;
cout.precision(2);
cout.setf(ios::fixed, ios::floatfield);
int i = 10, j = 20;
cout << "i, j = " << i << "," << j << ".\n";
cout << "Using complier-generated int swapper:\n";
Swap(i, j);
cout << "Now, i, j = " << i <<", " << j << ".\n";
job sue = {"Susan Yaffee", 73000.60, 7};
job sidney = {"Sidney Taffee", 78060.72, 9};
cout << "Before job swapping:\n";
Show(sue);
Show(sidney);
Swap(sue, sidney);
cout << "After job swapping:\n";
Show(sue);
Show(sidney);
// cin.get();
return 0;
}
template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}
template <> void Swap(job &j1, job &j2)
{
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}
void Show(job &j)
{
using namespace std;
cout << "j.name" << ": $" << j.salary
<< " on floor " << j.floor <<endl;
}

4. 实例化与具体化
实例化和具体化的区别:
实例化:编译器使用模板为特定类型生成函数定义。
具体化:仍然是一个模板,没有生成函数定义,需要在调用时编译器才生成函数定义。
隐式实例化:在调用函数时传入特定类型的参数,如向Swap(T &a, T&b)模板传入int类型的参数,编译器就生成使用int类型的示例。
显示实例化:直接命令编译器创建特定类型的示例,如下:
template void Swap<int>(int, int)
显示具体化声明:如下两种等价声明之一。与显示实例化声明的区别在于template后面有<>符号,显示实例化没有:
template <> void Swap<int> (int &, int &)
template <> void Swap(int &, int &)
5. 函数模板的发展
编写函数模板时,可能出现无法确定声明中使用何种类型的情况:
template<typename T1, typename T2>
void ft(T1 x, T2 y)
{
……
?type? xpy = x + y;
……
}
此种情况下不确定xpy的类型。
C++11新增关键字decltype可以解决此问题。decltype关键字这样使用:
int x;
decltype(x) y; // y与x类型相同
可以给decltype提供表达式,ft可以改写如下:
template<typename T1, typename T2>
void ft(T1 x, T2 y)
{
……
decltype(x + y)xpy = x + y;
……
}
decltype的使用说明:
decltype(expression) var;
(1) 如果expression是一个没有用括号括起来的标识符,则var的类型与标识符的类型相同,包括const等限定符
(2) 如果expression是一个函数调用,则var的类型与函数的返回类型相同
(3) 如果expression是一个左值,则var指向其类型的引用
(4) 如果以上都不满足,则var的类型与expression的类型相同

1062

被折叠的 条评论
为什么被折叠?



