模板进阶
1.非类型模板参数
模板参数分 类类型形参 与 非类型形参 。类型形参即:出现在模板参数列表中,跟在 class 或者 typename 之后的参数类型名称 。非类型形参,就是用一个常量作为类 ( 函数 ) 模板的一个参数,在类 ( 函数 ) 模板中可将该参数当成常量来使用。比如一下代码:namespace xcn { //定义一个模板类型的静态数组 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; }; } //T为类类型模板参数 N为非类型模板参数
注意:(1). 浮点数、类对象以及字符串 是不允许作为非类型模板参数的 。(2). 非类型的模板参数必须在编译期就能确认结果 。
2.函数模板特化
模板特化:
通常情况下, 使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 果 ,需要特殊处理,比如:实现了一个专门用来进行“等于”比较的函数模板。
class Date { public: Date(int year =1, int month = 1, int day = 1) :_year(year) ,_month(month) ,_day(day) {} bool operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } private: int _year; int _month; int _day; }; template<class T> bool Less(T left, T right) { return left == right; } int main() { cout << Less(1, 2) << endl; // 可以比较,结果正确 Date d1(2024, 8, 8); Date d2(2024, 8, 8); cout << Less(d1, d2) << endl; // 可以比较,结果正确 Date* p1 = new Date(2024, 8, 12); Date* p2 = new Date(2024, 8, 12); cout << Less(p1, p2) << endl; // 可以比较,结果错误 return 0; }
上述的p1和p2这两种指针类型,虽然可以比较,就无法保证结果的正确性,针对这个问题,就有了函数模板特化。
class Date { public: Date(int year =1, int month = 1, int day = 1) :_year(year) ,_month(month) ,_day(day) {} bool operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } private: int _year; int _month; int _day; }; template<class T> bool Less(T left, T right) { return left == right; } //针对某些类型要特殊化处理 template<> bool Less<Date*>(Date* left, Date* right) { return *left == *right; } int main() { cout << Less(1, 2) << endl; // 可以比较,结果正确 Date d1(2022, 7, 8); Date d2(2022, 7, 8); cout << Less(d1, d2) << endl; // 可以比较,结果正确 Date* p1 = new Date(2022, 7, 15); Date* p2 = new Date(2022, 7, 15); cout << Less(p1, p2) << endl; // 可以比较,结果正确 return 0; }
class Date { public: Date(int year =1, int month = 1, int day = 1) :_year(year) ,_month(month) ,_day(day) {} bool operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } private: int _year; int _month; int _day; }; template<class T> bool Less(T* left, T* right) { return *left == *right; } //函数模板基本不用特化方式处理,直接写一个具体类型的函数更好 int main() { cout << Less(1, 2) << endl; // 可以比较,结果正确 Date d1(2022, 7, 8); Date d2(2022, 7, 8); cout << Less(d1, d2) << endl; // 可以比较,结果正确 Date* p1 = new Date(2022, 7, 15); Date* p2 = new Date(2022, 7, 15); cout << Less(p1, p2) << endl; // 可以比较,结果正确 return 0; }
函数模板基本不用特化方式处理,直接写一个具体类型的函数更好:
一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给 出。bool Less(Date* left, Date* right) { return *left == *right; }
注意:该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。
3.类模板特化
全特化/半特化/偏特化
template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; // 全特化::将模板参数类表中的全部参数特化。 template<> class Data<int, double> { public: Data() { cout << "Data<int, double>" << endl; } }; // 半特化:将模板参数类表中的一部分参数特化。 template<class T1> class Data<T1, char> { public: Data() { cout << "Data<T1, char>" << endl; } }; // 偏特化:不仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。 template<class T1, class T2> class Data<T1*, T2*> { public: Data() { cout << "Data<T1*, T2*>" << endl; } }; template<class T1, class T2> class Data<T1&, T2&> { public: Data() { cout << "Data<T1&, T2&>" << endl; } };
4. 模板分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。如果模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义。经过预处理->编译->汇编->链接后,编译器会报出错误。// a.h template<class T> T Add(const T& left, const T& right); // a.cpp template<class T> T Add(const T& left, const T& right) { return left + right; } // main.cpp #include"a.h" int main() { Add(5, 2); Add(2.0, 5.0); return 0; }
解决方法:将声明和定义放到一个文件 "xxx.h" 里面。