array forward_list 模板进阶
1)arrary
静态数组
注意
:
浮点数
,类对象
以及字符串
是不允许
作为非类型模板参数的- 非类型的模板参数必须在编译期就能确认结果
在32位Linux下栈很小只有8MB
不建议使用
:如果数据量过大会造成栈溢出
对比vevtor
:vector只有12byte地址在栈上,开辟的空间都在堆上
参考:array
2)forward_list
单向链表
注意forward_list无尾插尾删
参考:forwa_list
3)模板的特化
概念
:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化
1.函数模板特化
模板的匹配原则
:(引入)
例:template<class T> bool IsEqual(const T& left, const T& right) { return left == right; } bool IsEqual(const char* const & left,const char* const & right) //bool IsEqual(const char* left, const char* right) // 函数模板的特化 (针对某些类型的特殊化处理) { return strcmp(left, right) == 0; } int main() { cout << IsEqual(1, 2) << endl; char p1[] = "dfddf"; char p2[] = "dfsdf"; cout << IsEqual<const char*>(p1, p2)<< endl;; return 0; }
注意
:
(const char* const & left,const char* const & right)
‘*’前面的const修饰的是*left,’*'后面的const才是修饰的left本身,而传入的形参p1,p2是数组名具有const属性 , 写为这样不能识别(const char*& left, const char*& right)
- 不能在
bool IsEqual(const T& left, const T& right)
内用if(T==const char*) return strcmp(left, right) == 0;
不支持T==const char*
此语法
函数模板特化
:template<> bool IsEqual<const char* const>(const char* const &left, const char* const &right) { return strcmp(left, right) == 0; }
注意
:
- 函数模板只能
全特化
不能偏特化- 函数模版的全特化版本不参与函数重载解析,并且优先级低于函数基础模版参与匹配
- 一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
- 出现下面的报错 因为本身存在类型转换,再引用const和&就会更复杂(不同编译器识别不同)改为 bool IsEqual<char*>(char* left,char* right)或直接重载即可(
传字符串会自动推导为char[字符个数]所以不会选中特化
)
参考:Why Not Specialize Function Templates(
为什么不特化函数模板?
)
2.类模板特化
①全特化
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; }; Data<int, int> d1; Data<int, char> 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; }; Data<int *, int*> d3; // 调用特化的指针版本 Data<int&, int&> d4(1, 2); // 调用特化的指针版本
4)模板分离编译
①分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
②函数模板分离编译
以下场景,模板的声明与定义分离开
- Func.h
#pragma once #include <iostream> using namespace std; template<class T> void F(const T& x); void Print();
- Func.cpp
#include "func.h" template<class T> void F(const T& x) { cout << "void F(const T& x)" << endl; } void Print() { cout << "void Print()" << endl; }
- Test.cpp
#include "Func.h" int main() { F(1); Print(); return 0; }
编译器报错:
分析
:
运行需要预处理
,编译
,汇编
,链接
预处理
:完成头文件包含,#define定义的符号和宏的替换,去除注释
每个文件分开预处理
Func.cpp
生成Func.i
template<class T> void F(const T& x); void Print(); template<class T> void F(const T& x) { cout << "void F(const T& x)" << endl; } void Print() { cout << "void Print()" << endl; }
在Func.cpp中编译器没有看到对F模板函数的实例化因此不会生成具体的F函数
Test.cpp
生成Test.i
template<class T> void F(const T& x); void Print(); int main() { F(1); Print(); return 0; }
编译
: 对程序按照语言特性进行词法、语法、语义分析,错误检查无误后生成汇编代码注意头文件不参与编译编译器对工程中的多个源文件是分离开单独编译的
汇编
: 生成汇编语言
两个调用函数F(1) Print()生成汇编代码
call _Z1Fi(待填入的地址)
call z5Print(待添入的地址)
链接
:将多个obj(目标)文件合并成一个,并处理没有解决的地址问题
只有在链接的时候才会进行合并在
Test.obj
中调用的F<int>
和Print()
,但函数F
没有实例化没有生成具体的代码因此链接时报错
解决方法1
:显式实例化
Test.cpp中添加下面的注释代码
#include "func.h" template<class T> void F(const T& x) { cout << "void F(const T& x)" << endl; } void Print() { cout << "void Print()" << endl; } //template //void F(const int& x);
缺点
: 用一个类型时,就需要再次显示实例化,麻烦
解决方法2
:不分离编译声明和定义或者直接定义在.h中
template<class T> void F(const T& x); template<class T> void F(const T& x) { cout << "void F(const T& x)" << endl; } void Print();
注意
: 源文件为.h
和.hpp
都可以
参考:为什么C++编译器不能支持对模板的分离式编译
③类模板其他问题
类模板分离编译和函数模板同理
.h源文件template<class T> class Stack { public: Stack(); ~Stack(); void Push(const T& x); private: T* _a //这里没有分号,编译器会报错 因为会对类框架进行检测 int _top; int _capacity; }; template<class T> Stack<T>::Stack() { _a = new T[10]; _top = 0; _capacity = 10; } template<class T> Stack<T>::~Stack() { delete[] _a; _a = nullptr; } template<class T> void Stack<T>::Push(const T& x) { _a[_top] = x //这里没有分号,没有实例化所以不会报错 ++_top; }
- 编译器会报错 因为会对类框架进行检测
- Push有语法问题,没有被检查出来,编译不报错,模板如果没有实例化,编译器不会去检查模板内部语法错误,我们实例化了栈这个类,对类模板是按需实例化,调用了哪个成员函数就实例化谁
5)模板总结
优点
:
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增强了代码的灵活性
缺陷
:
- 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误