去实现两个数求和:
1、C语言泛型——>void*。是一个半开半闭的区间,可能会导致越界;
2、函数重载。自定义类型众多,代码重复度高;
3、宏函数。宏函数没有类型检查和安全检查机制,在预编译期间处理,不安全;
4、C++中的泛型——>拿模板实现。
一、什么是模板?
模板是C++支持参数化多态的工具,使用模板可以使用户为类或函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
二、模板实现
(一)、函数模板
例子1:
template<typename T> //<>模板类型参数列表
T Sum(T a, T b)
{
return a + b;
}
/*
int Sum(int a, int b) 模板函数
{
return a + b;
}
double Sum(double a,double b) 模板函数
{
return a + b;
}
*/
int main()
{
Sum(10, 20); //模板的实参推演
Sum(10.1,20);//参数不匹配,在推演时导致模板参数"T"不明确,运行出错
int rt = Sum<int>(10, 20); //<int>模板实参列表
std::cout << "int :" << rt << std::endl;
double rt1 = Sum<double>(10.1, 20.1);
std::cout << "double :" << rt1 << std::endl;
return 0;
}
注意点:
1、模板的实例化:编译调用模板函数时,编译器会根据传入的参数自动推演出模板形参的类型,并生成对应的代码(这份代码被称为模板函数)。
2、模板编译——>在定义点只编译模板的头部,不检查内部语法;在调用点编译模板函数。
3、模板的实例化的参数替换过程是一个typedef的替换。
4、模板的实参推演限制:
- 不能让编译器产生二义性;
- 有实参才能进行推演。
例子2:
template<typename T>
void Sort(T* arr, int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
T tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
template<typename T,int LEN>
void Sort2(T* arr)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
T tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
double arr[] = {1.1,2.1,3,4,5.1,6,7,8};
int len = sizeof(arr)/sizeof(arr[0]);
Sort(arr,len); //Sort<double>(arr,len);
const int len2 = sizeof(arr)/sizeof(arr[0]);
Sort2<double,len2>(arr);//非类型参数给值
return 0;
}
<>中,不仅能存放模板类型参数(类型参数),还能存放非类型参数
5、模板的类型参数
typename、class都能定义
6、模板的非类型参数
非类型参数——>一个固定类型的常量,而不是一个类型
固定类型是有局限的,只有整形,指针和引用才能作为非类型形参,而且绑定到该形参的实参必须是常量表达式,即编译期就能确认结果。
非类型形参的限定要从两个方面看:
(1)对模板形参的限定,即template<>里面的参数
- 浮点数不能作为非类型形参,如:float、double;
- 类、字符串不可以作为非类型形参;
- 能转化为整型的类型可以作为形参(char、long、bool、unsigned…);
- 指向对象或函数的指针和引用可以作为形参。
(2)对模板实参的限定,即实例化时<>里面的参数
- 实参必须是编译时的表达式常量,不能使用非const的局部变量、局部对象地址和动态对象;
- 由于形参的已经做了限定,字符串,浮点型即使是常量表达式也不可以作为非类型实参。
例子3
template<typename T>//模板的版本
bool Compare(T a,T b)
{
return a > b;
}
template<>//模板的特例化版本——>内部也要进行编译
bool Compare<char *>(char* a,char* b)
{
return strcmp(a,b)>0;
}
template<typename T>
bool Compare(const T a, const T b)//const 修饰 a
{
std::cout << "template<typename T> :" << typeid(T).name() << std::endl;
return a > b;
}
template<>
//bool Compare<char*>(const char* a, const char* b) const修饰*a
bool Compare<char*>(char* const a, char* const b)//特例化版本的逻辑应该满足和模板版本相同的逻辑
{
std::cout << "template<> :" << typeid(a).name() << std::endl;
return strcmp(a, b) > 0;
}
7、模板的特例化——>模板的版本不能满足特殊类型的需求,特例化版本的逻辑应该满足和模板版本相同的逻辑
- 完全特例化(全特化)
- 部分特例化(偏特化)
函数模板仅支持完全特例化;类模板不仅支持完全特例化还支持部分特例化
8、模板的默认值
函数模板的默认值C++11特性
类模板默认值
9、不明确类型的返回值接收——>auto
例子4:
bool Compare(char* a, char* b)//普通函数版本
{
std::cout << "bool Compare(char*, char*) :" << typeid(a).name() << std::endl;
return strcmp(a, b) > 0;
}
template<typename T>//模板的版本
bool Compare(T a, T b)
{
std::cout << "template<typename T> :" << typeid(T).name() << std::endl;
return a > b;
}
template<>//模板的特例化
bool Compare<char*>(char* a, char* b)
{
std::cout << "template<> :" << typeid(a).name() << std::endl;
return strcmp(a, b) > 0;
}
int main()
{
Compare("hello", "world");//template<typename T> :char const *
Compare<>("hello", "world");//template<typename T> :char const *
char* p = "hello"; // = const char*
char* q = "world";
Compare(p, q);//bool Compare(char*, char*) :char *
Compare<>(p,q);//template<> :char *
return 0;
}
10、模板的重载
http://www.cnblogs.com/liyuan989/p/4138378.html
11.模板的显示实例化
模板最好将声明和定义放到一个文件 “xxx.h” 里面,因为,编译器编译时,将不同的文件编译生成不同的文件,而模板在编译时不进行类型检查,在运行时推演,这样会导致在链接过程中会链接不上。
(二)、类模板
1、构造函数和析构函数可以缺省类型参数,其它所有用到模板名称的地方都加上类型参数。
2、类模板的选择性实例化——>只有在调用时,对于调用的成员方法实例化,对于未调用的成员方法不做任何处理。
3、类模板的特例化
- 完全特例化——>较大部分函数不能满足某种类型的逻辑
- 部分特例化——>较小部分函数不满足某种类型的逻辑,部分函数进行特例化(必须要有模板)
template<typename T>
class Sum
{
public:
Sum(T first, T second) :_Left(first), _Right(second){}
T add()
{
std::cout << "template<typename T> T add()" << std::endl;
return _Left + _Right;
}
template<typename E>
E add()
{
std::cout << "template<typename E> E add()" << std::endl;
return _Left + _Right;
}
template<>
char* add()
{
std::cout << "template<> char* add()" << std::endl;
char* parr = new char[strlen(_Left) + strlen(_Right) + 1];
strcpy(parr, _Left);
strcat(parr, _Right);
return parr;
}
private:
T _Left;
T _Right;
};