STL: S标准 T:模板 L:库
模板的报错不准确
学习内容:
- 1、什么是模板?
- 2、模板的原理?
- 3、模板函数、模板类
- 4、非类型模板参数
- 5、特化–类型萃取
- 6、模板的分离编译
一、泛型编程
- 模板:模板是泛型编程的基础。所谓泛型编程就是编写与类型无关的逻辑代码,是一种复用方式。
- 分类:模板函数和模板类。
二、模板函数
1)- 模板函数
假设现在要实现一个比较两个数是否相等的重载函数。
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
template<class T>
void Swap(T* x1, T* x2)
{
T x = *x1;
*x1 = *x2;
*x2 = x;
}
int main()
{
int a = 1, b = 2;
Swap(&a, &b);
int *p1 = &a;
int *p2 = &b;
Swap(&p1, &p2);
char c1 = 1, c2 = 2;
Swap(&c1, &c2);
system("pause");
return 0;
}
第一组交换前:
第一组交换后:
第二组交换前:
第二组交换后:
void Swap(T* *x1, T** x2)
{
T x = **x1;
**x1 = **x2;
**x2 = x;
}
第三组交换前:
第三组交换后:
查看反汇编:
结论:
函数模板格式:
template<class 形参名1,class形参名2,class形参名n>
返回类型 函数名(参数列表)
{...}
- 模板形参的定义既可用class,也可使用typename,含义是相同的。
不同的函数推演出不同的模板类型,底层调用的是三个不同的函数类型。
模板函数只有被推演出来是什么类型才参与编译,否则不参与编译。没有被推演出来的话,它只检查函数的外壳即函数名哪一行,函数内部不检查。
“`
string 的底层汇编是basic_string
int 的底层汇编是int
2)模板参数匹配及显示实例化
由于传不同类型的参数会导致推演出的函数类型不匹配进而导致编译器报错,因此可以指定所传参数推演出的类型。如下图:
3)重载函数模块
本质是模板推演出的函数构成重载
template<typename T>
bool IsEqual(const T& left, const T& right)
{
return left == right;
}
template <typename T1,class T2>
bool IsEqual(const T1& left, const T2& right)
{
return left == right;
}
bool IsEqual(const int& left, const int& right)
{
return left == right;
}
int main()
{
/*string s1("s1");
string s2("s2");*/
cout << IsEqual(1, 2) << endl;
cout << IsEqual(1.2,1) << endl;
cout << IsEqual<double>(1.2, 1) << endl;
cout << IsEqual<int>(1.2, 1) << endl;
system("pause");
return 0;
}
它们构成重载,但是1、3两个编译器该调谁呢?
总结:
- 编译器调用如果有现成的就不会去推演,没有现成的就会去推演
- <>指定只能是模板函数
模板类:
类模板的格式
template<class 形参名1, class 形参名2, ...class 形参名n>
class 类名 { ... };
模板类实现动态顺序表:
https://mp.csdn.net/mdeditor/80299402
模板参数–实现容器适配器
template <typename T>
class SeqList
{
private :
int _size ;
int _capacity ;
T* _data ;
};
// template <class T, class Container>
template <class T, class Container = SeqList<T > //缺省参数
class Stack
{
public :
void Push (const T& x );
void Pop ();
const T & Top();
bool Empty ();
private :
Container _con ;
};
void Test ()
{
Stack<int > s1;
Stack<int , SeqList< int>> s2 ;
}
模板的模板参数–容器适配器
两种形式:
1)
2)
非类型的类模板参数
template<typename T,size_t MAX_SIZE=10>//带缺省模板参数
class SeqList
{
public:
SeqList();
private:
T_array[MAX_SIZE];
int _size;
};
template<typename T,size_t MAX_SIZE>
SeqList<T, MAX_SIZE>::SeqList()
:_size(0)
{}
void Test()
{
SeqList<int>s1;
SeqList<int, 20>s2;
}
图解:
扩展 面试题:
不要使用循环 */,实现1+2+3+…+n
1、实现 不要求
2、实现哦 o(1)–非类型模板参数
类型萃取
cout<<typeid(T).name()<<endl;
类型萃取:RTTI
1、T->int char double… 内置类型 memcpy
2、T->string… 自定义类型类型 for+operator=
RTTI:程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
模版的特化
代码:
1、特化之前:
template<class T>
class SeqList
{
public:
void Expand()
{
cout << "for+operator=" << endl;
}
};
int main()
{
SeqList<int> s1;
s1.Expand();
SeqList<string> s2;
s2.Expand();
system("pause");
return 0;
}
运行结果:
分析:
调用的全是for+operator=
2、特化:
代码:
template<>
class SeqList<int>
{
public:
void Expand()
{
cout << "memcpy" << endl;
}
};
结果:
分析:
调用了两种方式。
结论:2称之为1的特化
全特化
代码:
template < typename T1,typename T2>
class Data
{
public:
Data()
{
cout << "<T1,T2>" << endl;
}
private:
T1 _d1;
T2 _d2;
};
template<>
class Data<int,int>
{
public:
Data()
{
cout << "<int,int>" << endl;
}
private:
int _d1;
int _d2;
};
int main()
{
Data<int, int>d1;
Data<int, char>d2;
system("pause");
return 0;
}
结果:
结论:
- 有对应的类型则调对应的类型,
- 无对应的模型则调模版参数类型进行推演。
偏特化:
代码;
template<class T>
class SeqList
{
public:
void Expand()
{
cout << "for+operator=" << endl;
}
};
template < typename T1,typename T2>
class Data
{
public:
Data()
{
cout << "<T1,T2>" << endl;
}
private:
//T1 _d1;
//T2 _d2;
};
//全特化
template<>
class Data<int,int>
{
public:
Data()
{
cout << "<int,int>" << endl;
}
private:
int _d1;
int _d2;
};
//偏特化
template<class T1>
class Data<T1, int>
{
public:
Data()
{
cout << "<T1,int>" << endl;
}
private:
int _d1;
int _d2;
};
//偏特化(指针类型)
template<typename T1,typename T2>
class Data<T1*,T2*>
{
public:
Data()
{
cout << "<T1*,T2*>" << endl;
}
private:
};
//偏特化(引用类型)
template<typename T1, typename T2>
class Data<T1&,T2&>
{
public:
Data()
{
cout << "<T1&,T2&>" << endl;
}
private:
};
偏特化(引用类型)
//template<typename T1, typename T2>
//class Data<T1&, T2&>
//{
//public:
// Data()
// {
// cout << "<T1&,T2&>" << endl;
// }
//private:
//
//};
int main()
{
Data<int, int>d1;
Data<int, char>d2;
Data<char, int>d3;
Data<int*, int*>d4;
Data<char&, int&>d5;
Data<char*, int&>d6;
system("pause");
return 0;
}
POD类型萃取:
#include<iostream>
using namespace std;
#include<stdio.h>
#include<stdlib.h>
struct __TrueType
{};
struct __FalseType
{};
template < class T>
struct TypeTraits
{
typedef __FalseType IsPodType;
};
template<>
struct TypeTraits<int>
{
typedef __TrueType IsPodType;
};
template<class T>
T* __TypeCopy(const T* src, T* dst, size_t n,__TrueType)
{
cout << "memcpy" << endl;
return (T*)memcpy(dst, src, sizeof(T)*n);
}
template<class T>
T* __TypeCopy(const T* src, T* dst, size_t n,__FalseType)
{
for (size_t i = 0; i < n; i++)
{
dst[i] = src[i];
}
cout << "for+operator=" << endl;
return dst;
}
template<class T>
T* TypeCopy(const T* src, T* dst, size_t n)
{
return __TypeCopy(src, dst, n,TypeTraits<T>::IsPodType());
}
void TestCopy()
{
int srcint[3] = { 1, 2, 3 };
int dstint[3] = { 0 };
string srcstring[3] = { "11", "22", "33" };
string dststring[3];
TypeCopy(srcint, dstint, 3);
TypeCopy(srcstring, dststring, 3);
}
int main()
{
TestCopy();
system("pause");
return 0;
}
运行截图:
int 型:
string 型:
图解:
总结:
1、偏特化并不仅仅是指特殊部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
2、模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在。
模板的分离编译:
1、模板不支持分离编译,why?
分离编译不会模板实例化
2、怎样解决?
1)显示实例化,最好指定模板类型(因为有的模板必须指定类型)。
2)最好的方式是把声明和定义放一个头文件里(类里面定义太大了)。
模版总结:
优点:
- 1、模板复用了代码,节省资源,更快的迭代开发,c++的标准模版库(STL)因此而产生
- 2、增强了代码的灵活性。
缺点:
1、模版让代码变得凌乱复杂,不易维护,编译代码时间变长。
2、出现模板编译错误时,错误信息非常凌乱,不易定位错误。