在我们之前的学习中,我们学习了初阶的模板,函数模板、类模板,并学会了简单的运用,本章将继续深入学习模板的内容,模板进阶…
(1)先看一段代码:
void print_list(const list<int>& lt)
{
list<int>::const_iterator cit = lt.begin();
while (cit != lt.end())
{
cout << *cit << " ";
cit++;
}
cout << endl;
}
上述代码为打印一个链表中节点数据的代码。
(2)我们将上述函数改为函数模板:
template<class T>
void print_list(const list<T>& lt)
{
typename list<T>::const_iterator cit = lt.begin();
while (cit != lt.end())
{
cout << *cit << " ";
cit++;
}
cout << endl;
}
STL中list的迭代器的实现:
typname 告诉编译器后面这一串是一个类型,等Iterator实例化之后,再去它里面找这个内嵌类型。
(3)接着用一下适配器模式:
template<class Container>
void print_container(const Container& lt)
{
//typename Container::const_iterator cit = lt.begin();
//用auto则不用加typename,因为auto是根据返回值类型自动推导
//去类模板里面取的话都要加上typename
auto cit = lt.begin();
while (cit != lt.end())
{
cout << *cit << " ";
cit++;
}
cout << endl;
}
上述代码是容器适配器的思想,适配一个Container容器,同样的道理,容器虽然确定了,但是容器内的迭代器却是虚拟的。
整体模板的虚拟类型都没确定:
注意:
定义一个静态的栈:
#define N 100
template<class T>
class Stack
{
private:
T _a[N];
int _pop;
};
这里有个缺陷,这里的栈的大小是固定死的,每次创建出来的栈都是一样的大小。
问题:
模板让我们实现了同时可以创建不同数据类型的栈,但是不能实现不同数据个数的栈。
如下就是上述代码不能实现的功能:(缺陷)
我们这时候就引入了非类型模板参数:
直接见代码:
template<class T, size_t N = 100>
class Stack
{
public:
//非类型模板参数是不能修改的 -- 因为常量是不能修改的
void f()
{
N = 10;
}
private:
T _a[N];
int _pop;
};
C++引入了一个新的类模板 —— array
这里便用到了非类型模板参数,size_t N
array和vector的区别:
大小不一样:
array与vector的区别:
总结:
针对某些类型特殊化处理 - - 模板的特化
概念:
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。
**比如:**实现了一个专门用来进行小于比较的函数模板。
比较大小模板,直接见代码:
//模板的特化
template<class T>
bool Less(T left, T right)
{
return left < right;
}
//针对某些类型特殊化处理 -- 模板的特化
//首先要有一个基础的函数模板
//针对Date*进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right;
}
bool Less(Date* left, Date* right)
{
return *left < *right;
}
int main()
{
cout << Less(1, 2) << endl; //可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; //可以比较,结果正确
Date* p1 = new Date(2022, 7, 16);
Date* p2 = new Date(2022, 7, 15);
//这里比较的是地址
cout << Less(p1, p2) << endl;
//只用这种方式(p1, p2相比较)可以用模板的特化来解决
return 0;
}
日期类复习 – 传送门
此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。
问题的解决:
我们比较的是日期的大小,直接传日期类的对象就可以调用日期类的运算符重载,但是如果传的是日期类对象的指针,比较的就是地址的大小,显然不是我们想要的比较日期类对象的大小。如果在我们只传指针还要解决问题的限制下,这时应用模板特化就可以很好的解决这种问题。
函数模板的特化步骤:
注意:
(1)全特化:
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data" << endl; }
private:
T1 _d1;
T2 _d2;
};
//类模板的特化 -- 全特化(写死了)
template<>
class Data<int, double>
{
public:
Data() { cout << "Data" << endl; }
};
(2)偏特化(半特化):
//半特化 / 偏特化()半特化不是特化一半
//1、将部分模板参数列表中的一部分参数特化
template<class T1>
class Data<T1, char>
{
public:
Data() { cout << "Data" << endl; }
};
//2、偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//只要T1 和 T2是指针就走这个 -- 针对指针特殊化处理
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
Data() { cout << "Data" << endl; }
};
template<class T1, class T2>
class Data<T1&, T2&>
{
public:
Data() { cout << "Data" << endl; }
};
int main()
{
Data<int, int> d1;
Data<int, double> d2;
//只要第二个是char都会匹配:半特化/偏特化
Data<int, char> d3;
Data<char, char> d4;
//只要是两个指针
Data<int*, int*> d5;
Data<int*, char*> d6;
Data<int*, string*> d7;
Data<int*, void*> d8;
//void不是类型,但是void*是一个类型,void*是不能解引用不能++
Data<int*, int> d9;//匹配原生的指针
Data<int&, char&> d10;
return 0;
}
运行结果如下:
//模板的特化 -- 应用
template<class T>
struct Less
{
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
template<>
struct Less<Date*>
{
bool operator()(Date* x, Date* y) const
{
return *x < *y;
}
};
//偏特化
//只要是指针都走这里
template<class T>
struct Less<T*>
{
bool operator()(T* x, T* y) const
{
return *x < *y;
}
};
int main()
{
Date d1(2022, 7, 7);
Date d2(2022, 7, 6);
Date d3(2022, 7, 8);
vector<Date> v1;
v1.push_back(d1);
v1.push_back(d2);
v1.push_back(d3);
// 可以直接排序,结果是日期升序
sort(v1.begin(), v1.end(), Less<Date>());
vector<Date*> v2;
v2.push_back(&d1);
v2.push_back(&d2);
v2.push_back(&d3);
//可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
//此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
//但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
sort(v2.begin(), v2.end(), Less<Date*>());
vector<int*> v3;
v3.push_back(new int(3));
v3.push_back(new int(1));
v3.push_back(new int(2));
sort(v3.begin(), v3.end(), Less<int*>());
return 0;
}
这个问题在初识模板章节就已经讲解:
初识模板 – 传送门