零、前言:
看《STL源码剖析》这本书,发现到处是模板的影子。之前一直没怎么看模板,对它恨不熟悉,这台影响看书进度了。
工欲善其事必先利其器!模板主要以下知识点:
/*
模板
1.函数模板
1.函数模板
2.模板实例化
3.模板函数
4.模板的实参推演
5.模板的特例化
特例化版本 > 模板版本
2.类模板
函数模板
模板的编译时机
1.定义点 模板的头部
2.调用点 模板函数
*/
//模板的类型参数列表
/*
1.类型参数 typename / class
2.非类型参数
1.浮点型 类类型
2.常量
模板重载
1.普通函数
2.模板特例化
3.模板版本
*/
一、typename的意义
以下 template 声明式中 , class 和 typename有何区别?
template<class T> class Widget; //使用 "class"
template<typename T> class Widget; //使用 "typename"
答案:没有不同。当我们声明template 类型参数. class 和 typename 的意义完全相同。某些程序员始终比较喜欢class. 因为可以少打几个字。其他人(包括我〉比较喜欢 typename. 因为它暗示参数并非一定得是个class 类型。少数开发人员在接受任何类型时使用 typename. 而在只接受用户自定义类型时保留旧式的class。然而从 C++ 的角度来看,声明template 参数时,不论使用关键字class 或typename. 意义完全相同。
(摘自《Effective C++》第7章 条款42)
typename的用法:两个
1.定义模板类型参数:这个用法中, 同class的用法一样,上文也说明二者不差别
2.声明一个类型:主要在类模板中要注意,在模板中编译时,由于类模板的编译顺序,编译器可能不知道一个xxx是类型还是变量,所以需要用到typename去声明。
二、函数模板
1.为什么要有函数模板:节省代码
如下程序,实现了函数的重载,再试代码量大
bool Compare(int a, int b)
{
return a > b;
}
bool Compare(double a, double b)
{
return a > b;
}
写成模板如下:
template<typename T>
bool Compare(T a, T b)//const char* a const char* b
{
std::cout << "bool Compare(T,T)" << std::endl;
return a > b;
}
template<> //针对特殊情形,但一定要在通用版本存在时才能用
bool Compare(const char* a, const char* b)
{
return strcmp(a, b) > 0 ? true : false;
}
int main()
{
Compare<int>(10, 20);
Compare(10.1, 20.1);//double double
Compare("hello", "world");
return 0;
}
例子二:
template<typename T, int SIZE>
void Sort(T arr[])
{
//SIZE = 5;
T tmp = T();
for (int i = 0; i < SIZE - 1; i++)
{
for (int j = 0; j < SIZE - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
template<typename T>
void Show(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main()
{
//int arr[] = { 12, 332, 4556, 89, 872, 3 };
//int len = sizeof(arr) / sizeof(arr[0]);
//Sort<int, len>(arr);
//Show<int>(arr, 6);
int a = 10;
const int b = 20;
const int c = a;//常量退化成常变量
return 0;
}
2.实例化:
隐式实例化指的是:在使用模板之前,编译器不生成模板的声明和定义实例。只有当使用模板时,编译器才根据模板定义生成相应类型的实例。如:int i=0, j=1;swap(i, j); //编译器根据参数i,j的类型隐式地生成swap<int>(int &a, int &b)的函数定义。Array<int> arVal;//编译器根据类型参数隐式地生成Array<int>类声明和类函数定义。
显式实例化:当显式实例化模板时,在使用模板之前,编译器根据显式实例化指定的类型生成模板实例。如前面显示实例化(explicit instantiation)模板函数和模板类。其格式为:template typename function<typename>(argulist);template class classname<typename>;显式实例化只需声明,不需要重新定义。编译器根据模板实现实例声明和实例定义。
显示具体化:对于某些特殊类型,可能不适合模板实现,需要重新定义实现,此时可以使用显示具体化(explicite specialization)。显示实例化需重新定义。格式为:template<> typename function<typename>(argu_list){...};template<> class classname<typename>{...};
3.编译顺序:
三、类模板
1.用例、写法:
template<typename T>
class CLink
{
public:
CLink()
{
phead = new Node();
}
void insertHead(T val)
{
Node* pnewnode = new Node(val);
pnewnode->pnext = phead->pnext;
phead->pnext = pnewnode;
}
class Node;
Node* find(T val);
void Show()
{
Node* pCur = phead->pnext;
while (pCur != NULL)
{
std::cout << pCur->mdata << " ";
pCur = pCur->pnext;
}
std::cout << std::endl;
}
private:
class Node
{
public:
Node(T val = T()) :mdata(val), pnext(NULL){}
public:
T mdata;
Node* pnext;
};
Node* phead;
};
template<typename T>
typename CLink<T>::Node* CLink<T>::find(T val)//typename
{
Node* pCur = phead->pnext;
while (pCur != NULL)
{
if (pCur->mdata == val)
{
return pCur;
}
pCur = pCur->pnext;
}
return NULL;
}
int main()
{
CLink<int> cl;
for (int i = 0; i < 10; i++)
{
cl.insertHead(i + 1);
}
cl.Show();
auto rt = cl.find(6);
return 0;
}