模板

去实现两个数求和:
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、类模板的特例化

  1. 完全特例化——>较大部分函数不能满足某种类型的逻辑
  2. 部分特例化——>较小部分函数不满足某种类型的逻辑,部分函数进行特例化(必须要有模板)
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;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值