1.反向迭代器的优化
1.1优化目标
原来的模拟实现的反向迭代器需要传递三个模板类型
template<class Iterator, class ref, class ptr >
class Reverse_iterator
{
typedef Reverse_iterator<Iterator,ref,ptr > self;
...
}
而这一部分需要在具体的容器中进行定义,无法做到只有迭代器进行封装。如何解决这个问题?
1.2优化
对迭代器类进行处理
template<class T, class ref, class ptr >
struct list_iterator
{
typedef ref reference;
typedef ptr pointer;
.......
}
对方向迭代器进行处理
template<class Iterator>
class Reverse_iterator
{
public:
typedef Reverse_iterator<Iterator> self;
typename Iterator::reference operator*()
{
Iterator tmp = _it;
return *(--tmp);
}
typename Iterator::pointer operator->()
{
Iterator tmp = _it;
--tmp;
//返回的是迭代器内部数据类型的指针
return &(operator*(this));
}
}
类模板是虚拟类型,在没有实例化前不能去访问他里面内嵌的定义类型,编译器无法进行处理。
使用typename告诉编译器后面的内容是一个类型,待模板实例化以后,再确认具体的类型
template<class Iterator>
class Reverse_iterator
{
public:
typedef Reverse_iterator<Iterator> self;
Iterator::reference operator*()
{
Iterator tmp = _it;
return *(--tmp);
}
Iterator::pointer operator->()
{
Iterator tmp = _it;
--tmp;
//返回的是迭代器内部数据类型的指针
return &(operator*(this));
}
}
1.3typename的其他使用方式
比如构建一个list的打印模板函数
template <class T>
void print(list<T>& ls)
{
list<T>::const_iterator it = ls.begin();
while (it != ls.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
list属于虚拟类型,因此需要在前面加上typename告诉编译器。
template <class T>
void print(list<T>& ls)
{
typename list<T>::const_iterator it = ls.begin();
while (it != ls.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
1.4适合所有容器的打印函数
template <class container>
void print(container& ls)
{
typename container::const_iterator it = ls.begin();
while (it != ls.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
2.非类型模板参数
模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
//比如实现一个最大大小自定义的静态数组
//array开辟的空间是在栈上
template<class T,size_t N=100>
class array
{
public:
T& operator[](size_t pos)
{
return a[pos];
}
const T& operator[](size_t pos) const
{
return a[pos];
}
size_t size()
{
return _size;
}
bool empty()
{
return _size==0;
}
private:
T* a[N];
size_t _size;
}
注意:
浮点数、类对象和字符串类型都不能作为非类型模板参数;
非类型参数必须在编译期确定结果。
最常用的非类型参数是整形:size_t、int、char
3.函数模板特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理 。
template<class T>
bool Less(T a, T b)
{
return a < b;
}
void test()
{
Date d1=Date(2022, 7, 12);
Date d2=Date(2021, 7, 13);
cout << Less(d1,d2) << endl;
Date* p1 = new Date(2011,4,25);
Date* p2 = new Date(2011, 4, 24);
cout << Less(p1,p2) << endl;
}
这里d1和d2是调用的Date类型的比较运算符重载。而p1和p2比较的地址。如果想要正确的调用less,就需要对模板特化。
template<class T>
bool Less(T a, T b)
{
return a < b;
}
//特化
template<>
bool Less<Date*>(Date* a, Date*b)
{
return *a < *b;
}
void test()
{
Date d1=Date(2022, 7, 12);
Date d2=Date(2021, 7, 13);
cout << Less(d2,d2) << endl;
Date* p1 = new Date(2011,4,25);
Date* p2 = new Date(2011, 4, 24);
cout << Less(p1,p2) << endl;
}
//多个模板参数的特化
template<class T1,class T2>
void print(T1 a,T2 b)
{
cout << "print(T1,T2)" << endl;
}
template<>
void print<int, char>(int a, char b)
{
cout << "print(int,char)" << endl;
}
4.类模板特化
4.1全特化
全特化即是将模板参数列表中所有的参数都确定化 。
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;
};
void testData()
{
Data<int, int> d1;
Data<int, char> d2;
}
4.2偏特化
4.2.1部分模板参数类型特化
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
template<class T1>
class Data<T1, int>
{
public:
Data() { cout << "Data<T1,int>" << endl; }
T1 _d1;
int _d2;
};
void testData()
{
Data<int, int> d1;
Data<int, char> d2;
}
4.2参数更进一步的限制
//两个参数偏特化为指针类型
template <class T1, class T2>
class Data <T1*, T2*>
{
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template <class T1, class 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;
};
void test()
{
Data<int *, int*> d3; // 调用特化的指针版本
Data<int&, int&> d4(1, 2);//调用特化的引用版本
}
4.3迭代器的萃取
迭代器萃取:针对内置类型的迭代器进行特化;因为内置类型的迭代器就是原生的指针
对于内置类型:
将T*重命名为pointer,T&重命名为reference。这样无论是内置类型指针还是封装好的反向迭代器都可以适配迭代器。