前言:C++学习8:模板初步理解
设计类时,类中定义的类型暂时不指定。在之前的学习中有谈过,见模板入门
template<typename T>
class complex
{
public:
complex(T r = 0, T i = 0)
: re(r), im(i)
{}
complex& operator += (const complex&);
T real () const { return re; }
T imag () const { return im; }
private:
T re, im;
friend complex& __doapl (complex*, const complex&);
};
{
complex<double> c1(2.5, 1.5);
complex<int> c2(2, 6);
...
}
设计一个函数时,函数的形参和返回类型暂时不指定,要进行关键步骤:实参推导。在之前的学习中有谈过,见模板入门
template <class T>
inline const T& min(const T& a, const T& b)
{
return b < a ? b : a;
}
class stone
{
public:
stone(int w, int h, int we)
: _w(w), _h(h), _weight(we)
{ }
bool operator< (const stone& rhs) const
{ return _weight < rhs._weight; }
private:
int _w, _h, _weight;
};
语法:下面的程序中template
用途:可以想象外面的大模板是变化的东西,而在T1和T2确定之后,里面的小模板又可以变化,变化的类型是U1和U2。
作用:常用于构造函数,让构造函数可以构造的更灵活。
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair()
: first(T1()), second(T2()) {}
pair(const T1& a, const T2& b)
: first(a), second(b) {}
template <class U1, class U2>//成员模板,把U1构造为T1,U2构造为T2。故U1必须属于T1,U2必须属于T2
pair(const pair<U1, U2>& p)
: first(p.first), second(p.second) {}
};
对其进行使用,类比如下:
class Base1{}; //假设Base1为“鱼类”
class Derived1:public Base1{}; //假设Derived1为“鲫鱼”
class Base2{}; //假设Base2为“鸟类”
class Derived2:public Base2{}; //假设Derived2为“麻雀”
// T1 T2
pair<Derived1, Derived2>p; //制造一个p,把鲫鱼和麻雀放为一对
pair<Base1, Base2>p2(p); //制造一个p2,把鱼类和鸟类放为一对
//由上面一行的内容:可以构造出如下语句
// T1 T2 U1 U2
pair<Base1, Base2>p2(pair<Derived1, Derived2>());
由上例:鲫鱼和麻雀可以放入鱼类和鸟类中,而鱼类和鸟类不能放入鲫鱼和麻雀。(子类属于父类而父类不属于子类,即up-cast:向上层转换)
例,智能指针shared_ptr:
template<typename _Tp>
class shared_ptr:public __shared_ptr<_Tp>
{
...
template<typename _Tp1>
explicit shared_ptr(_Tpl* __p)
:__shared_ptr<_Tp>(__p){}
...
};
Base1* ptr = new Derived1; //向上层(父类)转换,是可以的,合理的(如指向鱼的指针指向鲫鱼,鲫鱼是鱼,故可以)
shared_ptr<Base1>sptr(new Derived1); //模拟up-cast
泛化即full specialization。特化的反义词是泛化。泛化是模板的特征,表示一个类型,用的时候再指定就行。
而特化针对某些独特的类型要特殊设计,就是把模板中的一个或几个类型给固定了下来。
形式:template<>,尖括号里面没有东西。
例子:
//泛化:
template <class Key>
struct hash{ };
//特化:
template<>
struct hash<char>
{
size_t operator() (char x) const { return x; }
};
template<>
struct hash<int>
{
size_t operator() (int x) const { return x; }
};
template<>
struct hash<long>
{
size_t operator() (long x) const { return x; }
};
使用就很简单了:
cout << hash() (1000);
泛化类型:
template<typename T, typename Alloc=...>
class vector
{
...
};
为了一种特殊的设计,将T绑定成了bool类型,可以偏这一个,对应定义一种特殊的设计。
template<typename Alloc=...>
class vector<bool, Alloc>
{
...
泛化类型:
template <typename T>
class C
{
...
};
若想只对指针类型进行特化处理,当使用者使用的类型只能是指针时,则应该调用下面这种特化版本。
template <typename U>
class C<U*>//只对指针类型进行特化处理
{
...
};
C<string> obj1; //使用的是上面那种泛化类型
C<string*> obj2; //使用的是当前这种特化类型
template<typename T,
template <typename T> //模板模板参数:本身是个模板参数,而这个参数又是模板
class Container //接上,模板模板参数:本身是个模板参数,而这个参数又是模板
>
class XCls
{
private:
Container<T> c;//拿第一个模板参数做参数
public:
......
};
template<typename T>
using Lst = list<T, allocator<T>>;
// T
XCls<string, list> mylst1;//想传入一个链表,让链表的元素作为string。但错误,模板有第二参数。
XCls<string, Lst> mylst2;//正确
另例:
template<typename T,
template <typename T> //模板模板参数
class SmartPtr //模板模板参数
>
class XCls
{
private:
SmartPtr<T> sp;
public:
XCls():sp(new T) { }
};
XCls<string, shared_ptr> p1;
XCls<string, unique_ptr> p2;//错误,原因,独自的特性
XCls<int, weak_ptr> p3;//错误,原因,独自的特性
XCls<long, auto_ptr> p4;
反例:第二模板参数不是模板了,绑定已被确定:
template <class T, class Sequence = deque<T>>
class stack
{
friend bool operator== <> (const stack&, const stack&);
friend bool operator< <> (const stack&, const stack&);
protected:
Sequence c;//底层容器
......
};
stack<int> s1;
stack<int, list<int>> s2;