模板
非类型模板参数
#define N 10 template<class T> //静态的数组 class Array { private: T _a[N]; }; int main() { Array<int> a1; //这样子数组已经写死了! Array<double> a2; //如果我们不想要10想要其他的大小那么我们就必须修改宏!这样子其实是不方便的! return 0; }
==如果我们想要一个能够灵活定义的固定数组
==这时候宏就不够方便灵活了!为了能够适应这种状况!于是就有了非类型模板参数!==
template<class T,size_t N = 10>//模板可以有缺省值 class Array { private: T _a[N]; }; int main() { Array<int,10> a1; Array<double,10000> a2; //这样就可以规定我们想要的固定的数组大小了! return 0; }
==非类型模板参数是一个整形常量!==
实际中C++的STL库的array里面也是使用非类型模板参数来实现了的!
==函数模板也是可以使用这个非类型模板参数==
template<class T,size_t N> void Func(const T& x) { cout << N << endl; } template<class T,double N> void Func2(const T& x) { cout << N << endl; } int main() { Func<int, 100>(1); FUnc2<int,1.00>(1); return 0; }
==非类型模板参数是不可以是非整形的!==
浮点数、类对象以及字符串是不允许作为非类型模板参数的。
template<class T,size_t N> void Func(const T& x) { cout << N << endl; } int main() { int a = 10; Func<int, a>(1); return 0; }
==非类型模板参数必须给的是一个常量!变量是不行的!==
模板的特化
函数模板特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板
例如下面的例子
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) { _cout << d._year << "-" << d._month << "-" << d._day; return _cout; } private: int _year; int _month; int _day; }; template<class T> bool Less(T x, T y) { return x < y; } int main() { cout << Less(1, 2) << endl; //结果正确 Date d1(2019, 1, 1); Date d2(2019, 1, 2); cout << Less(d1, d2) << endl; //结果正确 Date* p1 = &d1; Date* p2 = &d2; cout << Less(p1, p2) << endl; //可以比较但是结果错误 return 0; }
==为什么会发生这种情况呢?因为我们希望的比较的不是日期类的指针的地址的大小!而是希望比较日期类的内容!==
==所以如果能够对针对某些类型进行特殊处理那就是最好的!==
这种特殊处理我们就叫做特化!
template<class T> bool Less(T x, T y) { return x < y; } //对于Date*类型进行偏特化! template<> bool Less<Date*>(Date* x, Date* y) { return *x < *y; }
==这样子就可以符合我们想要的行为了!==
==下面的会去调用特化的哪一个!==
偏特化是对模板的特化,是一种特殊情况!
当然了也有人回想那为什么不直接写一个对应的函数?非得要什么偏特化?
bool Less(Date* x, Date* y) { return *x < *y; }
==虽然这样子也是可以的但是,如果遇到了类模板就不行了!函数模板能用这个解决是因为有函数重载!==
类模板的特化
全特化
template<class T1, class T2> class Base { public: Base() { cout << "Base<T1,T2>" << endl; } public: T1 _d1; T2 _d2; }; //假设如果我们想要对某种特定类进行特殊处理的话我们应该办? //函数模板可以写一个普通的函数出来!但是类就不可以了! //这就是对上面类的特化 template<> class Base<double, double> { public: Base() { cout << "Base<double,double>" << endl; } private: }; //里面的内容可以自由定义,只是说如果是double的参数,那么就是用这个特化的模板类!不一样是要一样的! int main() { Base<int, int> b1; Base<double, double> d1; return 0; }
另一个例子就是优先级队列的插入时候的比较
==我们看到如果我们使用的是data类的时候,我们就要显示的去写一个仿函数!==
但是有了类模板特化!我们就有了另一种新的解决办法!
==我们可以直接将less和greater等仿函数进行一次特化!我们不是对那种很大的类进行特化!我们一般是对仿函数之类的进行特化!==
上面的是全特化!就是对所有的参数进行特化!
半特化/偏特化
全特化的意思就是对全部的模板参数进行特化!
半特化就是只对部分的参数进行特化!
template<class T1, class T2> class Base { public: Base() { cout << "Base<T1,T2>" << endl; } public: T1 _d1; T2 _d2; }; template<>//全特化 class Base<double, char> { public: Base() { cout << "Base<double,char>" << endl; } private: }; template<class T>//偏特化 class Base<T, char> { public: Base() { cout << "Base<T,char>" << endl; } private: }; int main() { Base<int, int> b1; Base<int ,char> b2;//优先匹配偏特化 Base<long long ,char> b3;//优先匹配偏特化 Base<double,char> b4;//和全特化完全匹配!那么就匹配全特化! return 0; }
==如果偏特化更加的合适!那么就会优先去匹配半特化/偏特化!而不是原模板==
只要你满足偏特化的那个特化的参数那么就会去匹配偏特化!
==如果和全特化完全匹配那么就会优先去匹配全特化而不是偏特化!==
可以简单的记忆 就像是我们吃东西!有成品就吃成品,没有成品就吃半成品!
吃半成品就看那个半成品的参数匹配的更多!(比如有三个模板参数,有一个模板参数的偏特化,和两个模板参数的偏特化。如果能匹配上两个!那么就用两个的!而不是一个!)
只有什么都不匹配的时候,才去匹配原模板!
偏特化另一个用途——限制参数类型
template<class T1, class T2> class Base<T1*, T2*>//这是一种更加宽泛的偏特化!是对指针这个类型进行特化! { public: Base() { cout << "Data<T1*,T2*>" << endl; } }; int main() { Base<int, int> b1; Base<int*, int*> b3; Base<char*, char*> b4;//只要是指针都会走这个! return 0; }
template<class T1, class T2> class Base<T1&, T2&> { public: Base() { cout << "Data<T1&,T2&>" << endl; } }; int main() { Base<int, int> b1; Base<int&,int&> b2; Base<double&, double&> b3; return 0; }
==引用也是可以进行偏特化!==
==特化的本质体现了编译器的参数匹配原则==——==只要有这个参数的偏特化那么就优先使用这个参数的偏特化!==
有现成的吃现成的,没有现成的吃半成品,没有半成品最后才去自己搞
模板的优缺点
【优点】
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增强了代码的灵活性
【缺陷】
- 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误
- 对于分离编译支持不好!