C++--模板

函数模板和模板函数

定义:

函数模板可以用来创建通用的函数(该函数被称为模板函数),支持任意类型的形参和返回值,避免重载函数的函数体重复设计;

函数模板格式:

template<class 形参1, class 形参2, ... , class 形参n>

返回类型  函数名( 参数表 )

{

    ...

}

模板形参的定义既可以使用class,也可以使用typename,二者含义相同;

例:

#include<iostream>
#include<string>
using namespace std;

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;//判断是否相等
}

void test()
{
	int num1 = 1, num2 = 1;
	string s1("abc"), s2("bcd");

	cout<<IsEqual(num1,num2)<<endl;
	cout<<IsEqual(s1,s2)<<endl;
}

int main()
{
	test();
	return 0;
}

原理示意图:



从汇编角度看模板函数的推演:


模板参数匹配及显示实例化:

例:

#include<iostream>
using namespace std;

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}

int main()
{
	cout<<IsEqual(1, 2)<<endl;//正确,编译器确定T为int

	//cout<<IsEqual(1, 1.2)<<endl;//错误,编译器无法确定T为int还是double

	cout<<IsEqual<int>(1,1.2)<<endl;
	//正确,显示实例化,编译器确定T为int,传参过程会发生隐式转换

	cout<<IsEqual<double>(1, 1.2)<<endl;
	//正确,显示实例化,编译器确定T为double,传参过程会发生隐式转换

	return 0;
}

重载模板函数

例:

#include<iostream>
using namespace std;

bool IsEqual(const int& left, const int& right)
{
	return left == right;
}

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}

template<class T1, class T2>
bool IsEqual(const T1& left, const T2& right)
{
	return left == right;
}

int main()
{
	cout<<IsEqual(1, 2)<<endl;//调用第一个函数
	//当同时存在满足条件的模板函数和一般函数,优先调用一般函数

	cout<<IsEqual(1, 1.2)<<endl;//调用第三个函数

	cout<<IsEqual<int>(1,1.2)<<endl;//调用第二个函数
	//指明调用模板函数

	cout<<IsEqual<double>(1, 1.2)<<endl;//调用第二个函数
	//指明调用模板函数

	return 0;
}

当同时具有满足条件的模板函数和一般函数,优先调用一般函数;

类模板

类模板格式:

template<class 形参1, 形参2, ..., 形参n>

class 类名

{

    ...

};

例:

template<typename T>    //部分代码
class SeqList
{
public :
	SeqList();
	~ SeqList();
private :
	int _size ;
	int _capacity ;
	T* _data ;
};

template <typename T>
SeqList <T>:: SeqLis()
	: _size(0)
	, _capacity(10)
	, _data(new T[ _capacity])
{
}

template <typename T>
SeqList <T>::~ SeqList()
{
	delete [] _data ;
}

void test ()
{
	SeqList<int> s1;    //s1的类型为SeqList<int>
	SeqList<double> s2; //s2的类型为SeqList<double>
}

示意图:


模板参数--容器适配器

例:

#include<iostream>
using namespace std;

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 ;
	Stack<int , SeqList< char>> s3 ;
}
int main()
{
	test();
	return 0;
}

示意图:


模板的模板参数--容器适配器

例:

#include<iostream>
using namespace std;

template<typename T>
class SeqList
{
private :
	int _size ;
	int _capacity ;
	T* _data ;
};
// template <class T, template<class> class Container>
template<class T, template<class> class Container = SeqList> // 缺省参数
class Stack
{
public :
	void Push (const T& x );
	void Pop ();
	const T & Top();
	bool Empty ();
private :
	Container<T> _con ;
};
void test ()
{
	Stack<int > s1;
	Stack<int , SeqList> s2 ;
}
int main()
{
	test();
	return 0;
}

示意图:


非类型的函数模板/类模板参数

例1:

template<class T, int N>//非类型的函数模板参数
T add(const T& x)
{
	return x + N;
}

add<int,10>(1);

例2:

template<class T, size_t MaxSize>//非类型的类模板参数
class SeqList
{
private:
	_Data[MaxSize];
};

SeqList<int, 20> s;

模板的全特化/偏特化

函数模板的全特化

例:

#include<iostream>
using namespace std;

typedef struct Student
{
	char _name[5];
	int _age;
}Stu;

template<class T1, class T2>
bool IsEqual(T1 x, T2 y)
{
	return x == y;
}

template<>
bool IsEqual(const Stu& x, const Stu& y)//全特化函数模板
{
	return x._age == y._age;
}


int main()
{
	cout<<IsEqual(1,2)<<endl;

	Stu a = {"aaa",10};
	Stu b = {"bbb",10};
	cout<<IsEqual(a, b)<<endl;
	return 0;
}

函数模板的偏特化

严格来讲,函数模板不支持偏特化,由于可以对函数进行重载,所以可以达到类似于偏特化的效果;

类模板的全特化

例:

#include<iostream>
using namespace std;

template<typename T>//原模板
class A
{
public:
	A();
private:
	T _a;
};

//未特化的类模板在类外定义成员函数需要模板形参
template<typename T>
A<T>::A()
	:_a(0)
{
	cout<<"未全特化类模板"<<endl;
}

template<>//全特化类模板
class A<int>
{
public:
	A();
private:
	int _a;
};

//特化后在类外定义成员函数不需要模板形参
A<int>::A()
	:_a(0)
{
	cout<<"全特化的类模板"<<endl;
}

int main()
{
	A<char> a1;
	A<int> a2;
	return 0;
}

注意:

  • 类的成员函数只声明不定义,在调用时该函数时会出错,例如构造/析构函数;
  • 特化后的类模板在类外定义成员函数不需要模板参数;

类模板的偏特化

例1:

#include<iostream>
using namespace std;
template<typename T1,typename T2>
class A
{
public:
	A();
private:
	T1 _a;
	T2 _b;
};

template<typename T1, typename T2>
A<T1,T2>::A()
{
	cout<<"A<T1,T2>"<<endl;
}

template<typename T1>//偏特化的类模板
class A<T1,int>
{
public:
	A();
private:
	T1 _a;
	int _b;
};

template<typename T1>
A<T1,int>::A()
{
	cout<<"A<T1,int>"<<endl;
}

int main()
{
	A<int,char> a1;
	A<char,int> a2;
	return 0;
}

例2:

#include<iostream>
using namespace std;

template<typename T1,typename T2>
class A
{
public:
	A();
private:
	T1 _a;
	T2 _b;
};

template<typename T1, typename T2>
A<T1, T2>::A()
{
	cout<<"A<T1,T2>"<<endl;
}

template<typename T1,typename T2>//偏特化的模板类,模板参数为指针
class A<T1*,T2*>
{
public:
	A();
private:
	T1 _a;
	T2 _b;
};

template<typename T1,typename T2>
A<T1*,T2*>::A()
{
	cout<<"A<T1*,T2*>"<<endl;
}

template<typename T1, typename T2>//偏特化的模板类,模板参数为引用
class A<T1& ,T2&>
{
public:
	A();
private:
	T1 _a;
	T2 _b;
};

template<typename T1, typename T2>
A<T1&, T2&>::A()
{
	cout<<"A<T1&, T2&>"<<endl;
}

int main()
{
	A<int,double> a1;

	A<int*, int*> a4;
	A<int&, int&> a5;
	return 0;
}

注意:

  • 偏特化并不仅仅是指特殊部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本;
  • 模板的全特化和偏特化都是在已经定义的模板基础之上的,不能单独存在;
  • 当模板和特化模板同时满足实例化时,优先匹配特化模板;

模板的分离编译

模板不支持分离编译,原因是当一个模板不被用到时就不应该被实例化出来,在分离编译中,定义模板的文件中并没有实例化出模板,也就是没有该模板的代码,使得链接时找不到该模板的代码,也就无法实例化;

解决办法:将声明和定义放到同一个“.hpp”文件中去;

模板的优/缺点

优点:

1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生;

2. 增强了代码的灵活性;

缺点:

1. 模板让代码变得凌乱复杂,不易维护,编译代码时间变长;
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值