目录
1、非类型模板参数
模板参数分为类类型模板参数 与非类型模板参数。
类类型模板参数:出现在模板参数列表中,跟在class或者typename之后的参数类型名称。
非类型模板参数:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。(只支持整形家族)
例如:
cpp
// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class Array
{
public:
T& operator[](size_t index)
{
return _array[index];
}
const T& operator[](size_t index) const
{
return _array[index];
}
size_t size() const
{
return _size;
}
bool empty() const
{
return 0 == _size;
}
private:
T _array[N];
size_t _size;
};
int main()
{
Array<int> arr1;
Array<int, 100> arr2;
return 0;
}
注意:
1、浮点数、类对象以及字符串是不允许作为非类型模板参数的。 2、非类型的模板参数必须在编译期就能确认结果。
2、array容器
cpp
template < class T, size_t N > class array;
在C++的库中有一个array容器,该容器的底层就是固定大小的数组。
与直接使用固定大小的数组相比,使用array会更加的安全,例如:
cpp
int main()
{
array<int, 10> arr1;
int arr2[10];
arr1[15] = 0; // 运行会报错
arr2[15] = 0; // 运行不会报错
return 0;
}
该容器的使用非常简单,不再过多赘述。
3、模板的特化
3.1、概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化 与类模板特化。
3.2、函数模板的特化
函数模板的特化步骤:
1、必须要先有一个基础的函数模板(也就是说特化需要有原模板)
2、关键字template后面接一对空的尖括号<>
3、函数名后跟一对尖括号,尖括号中指定需要特化的类型
4、特化的函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
例如:
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{
}
bool operator<(const Date& d) const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d) const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
// 函数模板
template<class T>
bool Less(T left, T right)
{
return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(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 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
return 0;
}
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。例如:
cpp
bool Less(Date* left, Date* right)
{
return *left < *right;
}
该种实现简单明了,代码的可读性高,容易书写,因此函数模板一般不建议特化。
3.3、类模板的特化
3.3.1、全特化
全特化即是将模板参数列表中所有的参数都确定化。例如:
cpp
template<class T1, class T2>
class Data
{
public:
Data()
{
cout << "Data<T1, T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
template<>
class Data<int, char>
{
public:
Data()
{
cout << "Data<int, char>" << endl;
}
private:
int _d1;
char _d2;
};
int main()
{
Data<int, int> d1;
Data<int, char> d2;
return 0;
}
3.3.2、偏特化
偏特化任何针对模版参数进一步进行条件限制设计的特化版本。比如对于如下的模板类:
cpp
template<class T1, class T2>
class Data
{
public:
Data()
{
cout << "Data<T1, T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
偏特化有以下两种表现方式:
部分特化:将模板参数类表中的一部分参数特化。例如:
cpp
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
Data()
{
cout << "Data<T1, int>" << endl;
}
private:
T1 _d1;
int _d2;
};
参数更进一步的限制:偏特化并不仅仅是指特化部分参数,还指针对模板参数更进一步的条件限制所设计出来的一个特化版本。例如:
cpp
// 两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data()
{
cout << "Data<T1*, T2*>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout << "Data<T1&, T2&>" << endl;
}
private:
const T1& _d1;
const T2& _d2;
};
例如:
cpp
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
// 部分特化,将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
Data() { cout << "Data<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
// 两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data()
{
cout << "Data<T1*, T2*>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout << "Data<T1&, T2&>" << endl;
}
private:
const T1& _d1;
const T2& _d2;
};
int main()
{
Data<double, int> d1; // 调用特化的int版本
Data<int, double> d2; // 调用基础的模板
Data<int*, int*> d3;// 调用特化的指针版本
Data<int&, int&> d4(1, 2); // 调用特化的引用版本
return 0;
}
再比如:
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d) const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d) const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
// 特化一下,当T是Date*时,进行特殊处理,按指向的对象进行比较
template<>
class Less<Date*>
{
public:
bool operator()(Date* x, Date* y)
{
return *x < *y;
}
};
int main()
{
priority_queue<Date*, vector<Date*>, Less<Date*>> q2;
q2.push(new Date(2018, 10, 29));
q2.push(new Date(2018, 10, 28));
q2.push(new Date(2018, 10, 30));
cout << *q2.top() << endl;
return 0;
}
4、分离编译
4.1、概念
一个项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
4.2、函数模板的分离编译
例如:
head.h:
cpp
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
function.c:
cpp
#include "head.h"
template<class T>
T Add(const T& left, const T& right)
{
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
main.c:
cpp
#include "head.h"
int main()
{
Add(1, 2);
return 0;
}
尝试在VS下运行该程序会出现链接错误。原因:C或C++程序要运行,一般要经历以下几个步骤:
1、预处理:头文件展开、宏替换等。
2、编译:检查语法,生成汇编代码。
3、汇编:将汇编代码转成二进制机器码。
4、链接:将多个文件合并成一个,并处理没有解决的地址问题。
因为function.c中的模板函数没有实例化,因此不会生成具体的加法函数,因此也就没有对应的Add地址,在第四步链接的时候,main.c中要找对应的Add函数的地址,但是没有找到,因此就出现了链接错误。
注意:头文件不参与编译。编译器对多个源文件是分开单独编译的,然后通过链接形成最终的可执行程序。
解决办法:显示实例化将function.c改为如下即可
cpp
#include "head.h"
template<class T>
T Add(const T& left, const T& right)
{
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
// 显式实例化
template
int Add<int>(const int& left, const int& right);
4.3、类模板的分离编译
例如:
head.h:
cpp
#include <iostream>
using namespace std;
template<class T>
class Stack
{
public:
void Push(const T& x);
void Pop();
private:
T* _a = nullptr;
int _top = 0;
int _capacity = 0;
};
function.c:
cpp
#include "head.h"
template<class T>
void Stack<T>::Push(const T& x)
{
cout << "void Stack<T>::Push(const T& x)" << endl;
}
template<class T>
void Stack<T>::Pop()
{
cout << "void Stack<T>::Pop()" << endl;
}
main.c:
cpp
#include "head.h"
int main()
{
Stack<int> st;
st.Push(12);
st.Pop();
return 0;
}
尝试在VS下运行该程序会出现链接错误。错误原因和函数模板的分离编译的错误原因是一样的。
解决办法:显示实例化将function.c改为如下即可
cpp
#include "head.h"
template<class T>
void Stack<T>::Push(const T& x)
{
cout << "void Stack<T>::Push(const T& x)" << endl;
}
template<class T>
void Stack<T>::Pop()
{
cout << "void Stack<T>::Pop()" << endl;
}
// 显示实例化
template
class Stack<int>;
4.4、分离编译总结
不建议将模板的声明和定义分离到两个文件中,直接写在一个文件中即可,也就是头文件中(头文件以.h和以.hpp为后缀都可以,以.hpp为后缀有更强的寓意,表明在该头文件中,既有以.cpp为后缀的文件的内容也有以.h为后缀的文件的内容)。
例如:
head.h:
cpp
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right)
{
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
template<class T>
class Stack
{
public:
void Push(const T& x);
void Pop();
private:
T* _a = nullptr;
int _top = 0;
int _capacity = 0;
};
template<class T>
void Stack<T>::Push(const T& x)
{
cout << "void Stack<T>::Push(const T& x)" << endl;
}
template<class T>
void Stack<T>::Pop()
{
cout << "void Stack<T>::Pop()" << endl;
}
main.c:
cpp
#include "head.h"
int main()
{
Add(1, 2);
Stack<int> st;
st.Push(12);
st.Pop();
return 0;
}
或者干脆直接就声明和定义不分离了,写在一起。
4.5、模板总结
优点:
1、模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2、增强了代码的灵活性。
缺陷:
1、模板会导致代码膨胀问题,也会导致编译时间变长。
2、出现模板编译错误时,错误信息非常凌乱,不易定位错误。(看报错时,优先看第一个报错,因为后面的报错有可能就是前面的错误引起的)。
5、反向迭代器
不同的容器有不同的底层结构,比如:数组的结构、链表的结构、树形的结构等等。理论上讲我们使用容器要懂容器的底层结构,这样用STL的成本就会变高,迭代器的设计封装屏蔽了底层的实现的很多复杂细节,用统一简单的方式访问容器。
迭代器的实现有简单的方式,也有复杂的方式,比如:vector里面就可能是一个原生指针,当然也可能不是(VS下的vector的迭代器就不是原生指针);list的迭代器就不再是原生指针,而是自定义的,然后对这个自定义类型重载运算符。
通过前面对迭代器的使用,我们知道,反向迭代器的++就是正向迭代器的--,反向迭代器的--就是正向迭代器的++,因此反向迭代器的实现可以通过正向迭代器来实现。
5.1、list的反向迭代器
5.1.1、第一种设计
其反向迭代器的指向如下图所示:

例如:List.h
cpp
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _data;
list_node(const T& x = T())
:_prev(nullptr)
,_next(nullptr)
,_data(x)
{}
};
// ///////////////////////////////////////////////////////////
template<class T,class Ref,class Ptr>
struct __list_iterator
{
typedef list_node<T> Node;
typedef __list_iterator<T,Ref,Ptr> self;//这里的拷贝构造函数和析构函数都没有写,默认的就够用的了。
Node* _node;
__list_iterator(Node* node)
:_node(node)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self& operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
bool operator==(const self& s)
{
return _node == s._node;
}
};
// ///////////////////////////////////////////////////////
template<class T>
class List
{
public:
typedef list_node<T> Node;
// 正向迭代器
typedef __list_iterator<T,T&,T*> iterator;
typedef __list_iterator<T,const T&,const T*> const_iterator;
//反向迭代器
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
///////////////////////////////////////////////////////
//反向迭代器1
reverse_iterator rbegin()
{
return reverse_iterator(--end());
}
reverse_iterator rend()
{
return reverse_iterator(end());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(--end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(end());
}
// //////////////////////////////////////////////////////
iterator begin()
{
return _head->_next;//这里也可以写为:iterator(_head->_next);
}
iterator end()
{
return _head;//这里也可以写为:iterator(_head);
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
// ///////////////////////////////////////////////////////////
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
List()
{
empty_init();
}
List(const List<T>& lt)
{
empty_init();
const_iterator it = lt.begin();
while (it != lt.end())
{
push_back(*it);
++it;
}
//像下面这样写也是可以的
/*for (auto e : lt)
{
push_back(e);
}*/
}
/*List<T>& operator=(const List<T>& lt) // 传统写法
{
if (this != <)
{
clear();
for (auto e : lt)
{
push_back(e);
}
}
return *this;
}*/
void swap(List<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
List<T>& operator=(List<T> lt) // 现代写法
{
swap(lt);
return *this;
}
iterator insert(iterator pos, const T& x)
{
Node* newnode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
~List()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
delete cur;
prev->_next = next;
next->_prev = prev;
--_size;
return iterator(next);
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* end = _head->_prev;
end->_next = newnode;
newnode->_prev = end;
newnode->_next = _head;
_head->_prev = newnode;
_size++;
}
size_t size()
{
return _size;
}
private:
Node* _head;
size_t _size;
};
reverse_iterator.h
cpp
//反向迭代器(由正向迭代器实现出来的),这个东西本质上是一种迭代器适配器
template<class Iterator,class Ref,class Ptr>
class ReverseIterator
{
public:
typedef ReverseIterator<Iterator,Ref,Ptr> Self;
ReverseIterator(Iterator it)
:_it(it)
{}
Self& operator++()
{
--_it;
return *this;
}
Ref operator*()
{
return *_it;
}
Ptr operator->()
{
return _it.operator->();
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
private:
Iterator _it;
};
5.1.2、仿照库中迭代器的设计
库中迭代器设计的指向和上面的设计不同,其反向迭代器的指向如下图所示:这种设计是对称的

reverse_iterator.h:
cpp
//反向迭代器(由正向迭代器实现出来的)
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
ReverseIterator(Iterator it)
:_it(it)
{}
Self& operator++()
{
--_it;
return *this;
}
Self& operator--()
{
++_it;
return *this;
}
Ref operator*()
{
Iterator cur = _it;
return *(--cur);
}
Ptr operator->()
{
return &(operator*());
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
bool operator==(const Self& s)
{
return _it == s._it;
}
private:
Iterator _it;
};
并将List.h中反向迭代器的部分改为下面的代码即可:
cpp
//反向迭代器2
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}
5.2、vector的反向迭代器
5.2.1、第一种设计
其反向迭代器的指向如下图所示:

例如:Vector.h
cpp
template<class T>
class Vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//反向迭代器
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
public:
//////////////////////////////////////////////////////////////////////////////////
reverse_iterator rbegin()
{
return reverse_iterator(end() - 1);
}
reverse_iterator rend()
{
return reverse_iterator(begin()-1);
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end()-1);
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin()-1);
}
// ///////////////////////////////////////////////////////////////////////////////
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
// ////////////////////////////////////////////////////////////////////////////////
//构造函数
Vector()//没有参数的构造函数
{}
Vector(const Vector<T>& v)
{
reserve(v.capacity());
for (auto& e : v)
{
push_back(e);
}
}
Vector(size_t n, const T& val = T())
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
template<class InputIterator>
Vector(InputIterator first, InputIterator last)
{
while (first != last)
{
push_back(*first);
++first;
}
}
Vector(int n, const T& val = T())
{
reserve(n);
for (int i = 0; i < n; ++i)
{
push_back(val);
}
}
void swap(Vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
Vector<T>& operator=(Vector<T> tmp)//赋值重载
{
swap(tmp);
return *this;
}
~Vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
// ///////////////////////////////////////////////////
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
size_t sz = size();
if (_start)
{
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i];//之所以这样写是因为考虑到了自定义类型的情况。
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
//在c++这一块,内置类型也是有类似自定义类型中构造函数这样的概念。这一点要注意,其实都是类,都是面向对象的。
void resize(size_t n, const T& val = T())
{
if (n <= size())
{
_finish = _start + n;
}
else
{
reserve(n);
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator it = pos + 1;
while (it < _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
private:
iterator _start = nullptr;
iterator _finish = nullptr;
iterator _endofstorage = nullptr;
};
reverse_iterator.h:
cpp
//反向迭代器(由正向迭代器实现出来的)
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
ReverseIterator(Iterator it)
:_it(it)
{}
Self& operator++()
{
--_it;
return *this;
}
Ref operator*()
{
return *_it;
}
Ptr operator->()
{
return _it.operator->();
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
private:
Iterator _it;
};
5.2.2、仿照库中迭代器的设计
库中反向迭代器的设计是和正向迭代器对称的,其反向迭代器的指向如下图所示:

reverse_iterator.h:
cpp
//反向迭代器(由正向迭代器实现出来的)
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
ReverseIterator(Iterator it)
:_it(it)
{}
Self& operator++()
{
--_it;
return *this;
}
Self& operator--()
{
++_it;
return *this;
}
Ref operator*()
{
Iterator cur = _it;
return *(--cur);
}
Ptr operator->()
{
return &(operator*());
}
bool operator!=(const Self& s)
{
return _it != s._it;
}
bool operator==(const Self& s)
{
return _it == s._it;
}
private:
Iterator _it;
};
并将Vector.h中反向迭代器的部分改为下面的代码即可:
cpp
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
}