C++模板篇

一、概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本

1、函数模板格式:

(1)

template<typename T1, typename T2,......,class Tn>  T告诉编译器函数模板的类型为T(不确定的类型)。模板的参数列表位置一般表示的是类型,多个类型之间必须用逗号隔开,每个类型前面必须加上typename或者class,但是不能用struct。模板参数列表中,不同类型之间不能重名。
返回值类型 函数名(参数列表){}
template<class T>
//template<typename T>
T Add(T left, T right)
{
	return left + right;
}

(2)函数模板可以给位内联类型(inline):关键字inline在模板函数的返回值之前,参数列表之后

template<class T>
//template<typename T>
inline T Add(T left, T right)
{
	return left + right;
}

函数模板本身不是类或者函数

template<class T>
 inline T Add(T left, T right)
{
	return left + right;
}
int main()
{
	Add(1, 2);
	Add(2.3, 1.1);
	Add('1', '2');
	system("pause");
	return 0;
}

函数模板T并不知道是什么类型,进行实例化之后,编译期间根据参数类型对T进行推演判定T的类型。

a、查看参数的类型(显示实例化,根据实参的类型推演T的类型)

template<class T>
 inline T Add(T left, T right)
{
	 cout << typeid(left).name() << endl;
	return left + right;
}
int main()
{
	Add(1, 2);
	Add(2.3, 1.1);
	Add('1', '2');
	system("pause");
	return 0;
}

b、但是两个参数给定不同的类型会产生错误,编译器不清楚是把给成两个类型中的哪一种类型,编译器在推演期间不会进行类型转化。

c、为了解决上述问题,我们可以给一个<>,此处不需要对参数进行推演。显示的实例化。

int main()
{
	Add(1, 2);
	Add(2.3, 1.1);
	Add<int>('1', 2);
//      Add(1,(int)'2');
	system("pause");
	return 0;
}

3、类型转化:

(1)const转换:接收const引用或者const指针的函数可以分别用非 const对象的引用或者指针来调用

template<class T>
void Test(const T& a)
{}

int main()
{
	int b = 10;
	int &rb = b;
	Test(rb);
}

(2)数组或函数的转换:数组实参将转换为指向其第一个元素的指针函数 实参将转换为当做指向函数类型的指针

int Test1(int)
{
	return 0;
}
template<class T>
void Test(T a)
{
	cout << typeid(a).name() << endl;
}
int main()
{
	int arr[] = { 2, 1, 4, 5, 6, 7, 8, 3, 4 };
	Test(arr);
	Test(Test1);
	system("pause");
	return 0;
}

二、模板参数

1、类型形参:例如给定的模板T,不明确,模板形参。 

a、模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用, 遵循名字屏蔽规则(模板函数使用的是模板函数内部的模板函数,而不是模板参数外部函数的)(但是模板参数中的T只能在模板参数里面使用,出了模板函数则不能使用)。

b、 模板形参的名字在同一模板形参列表中只能使用一次:且需要加上class或者typename。

c、在函数模板的内部不能指定缺省的模板实参

2、非类型形参:参数的整型……

非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以 使用非模板类型参数

template<class T>
void PrintArray(const T* array, size_t size)
{
	for (size_t i = 0; i < size; ++i)
		cout << array[i] << "";
	cout << endl;
}
int main()
{
	int array[] = { 1, 2, 4, 5, 7, 4, 33, 9, 0 };
	int array1[] = { 2, 3, 1, 5, 6 };
	PrintArray(array, sizeof(array) / sizeof(array[0]));
	PrintArray(array1, sizeof(array1) / sizeof(array1[0]));
	system("pause");
	return 0;
}

或者采用模板参数,非类型的模板参数,类型已经具体了,此处为size_t

template<class T,size_t N>
void PrintArray(T(&array)[N])//数组的引用
{
	N = 10;
}
int main()
{
	int array[] = { 1, 2, 4, 5, 7, 4, 33, 9, 0 };
	int array1[] = { 2, 3, 1, 5, 6 };
	PrintArray(array);
	PrintArray(array1);
	system("pause");
	return 0;
}

模板参数中定义一个非类型的模板参数,一般认为是常量,给常量赋值会发生错误,改进:

template<class T,size_t N>
void PrintArray(T(&array)[N])//数组的引用
{
	for (size_t i = 0; i < size; ++i)
		cout << array[i] << "";
	cout << endl;
}
int main()
{
	int array[] = { 1, 2, 4, 5, 7, 4, 33, 9, 0 };
	int array1[] = { 2, 3, 1, 5, 6 };
	PrintArray(array);//PrintArray<int,9>
	PrintArray(array1);//PrintArray<int,5>
	system("pause");
	return 0;
}PrintArray<int,9>
	PrintArray(array1);//PrintArray<int,5>
	system("pause");
	return 0;
}

a、比较两个数的大小:

template<class T>
const T& Max(const T& left, const T& right)
{
	return left > right ? left :right;
}
int main()
{
	cout << Max(1, 2) << endl;
	cout << Max('a', 'b') << endl;
	system("pause");
	return 0;
}

b、比较三个:重载

template<class T>
const T& Max(const T& left, const T& right)
{
	return left > right ? left :right;
}
template<class T>
const T& Max(const T& left, const T& middle, const T& right)
{
	const T& ret = Max(left, middle);
	return Max(ret, right);
}
int main()
{
	cout << Max(1, 2) << endl;
	cout << Max('a', 'b') << endl;
	cout << Max(1, 2, 3) << endl;
	system("pause");
	return 0;
}

:函数的所有重载版本的声明都应该位于该函数被调用位置之前  

3、

(1)一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模 板还可以被实例化为这个非模板函数 

template<class T>
const T& Max(const T& left, const T& right)
{
	return left > right ? left :right;
}
int Max(int left, int right)
{
	return left > right ? left : right;
}
template<class T>
const T& Max(const T& left, const T& middle, const T& right)
{
	const T& ret = Max(left, middle);
	return Max(ret, right);
}
int main()
{
	cout << Max(1, 2) << endl;
	cout << Max('a', 'b') << endl;
	cout << Max(1, 2, 3) << endl;
	system("pause");
	return 0;
}

此时优先调用自己写的函数,而不是通过模板生成。

b、可以通过<>,来使优先调用模板函数

int main()
{
	cout << Max<>(1, 2) << endl;
	cout << Max('a', 'b') << endl;
	cout << Max(1, 2, 3) << endl;
	system("pause");
	return 0;
}

自己写的函数和 模板函数可以共存,优先调用自己写的函数,除了自己写的函数解决不了,此时若是要自动生成,要利用<>。告诉编译器去调用一个编译器生成的函数。相当于隐式的实例化,参数后面没有跟类型,需要通过推演获得参数的类型。

(2)对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调动非模板函数而不会从该模板产生出一个实例。如果模板可以 产生一个具有更好匹配的函数, 那么将选择模板  

template<class T1,class T2>
T1 Add(const T1& left, const T2& right)
{
	return left + right;
}
int Add(const int& left, const int& right)
{
	return left + right;
}
int main()
{
	Add(1, 2);
	Add(1, '2');
	system("pause");
	return 0;
}

此时调用编译器合成的参数,不用进行类型转化,直接调用模板,合成一个匹配度更高的。

(3)显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,而且所有的模板参数都应该根据实参演绎出来 

(4)模板函数大部分不允许自动类型转换,但普通函数可以进行自动类型转换

那么如何比较两个字符串的大小呢?

template<class T>
T Max(T left, T right)
{
	return left > right ? left : right;
}
int main()
{
	char *p1 = "niha";
	char *p2 = "udk";
	cout << Max(p1, p2) << endl;
	system("pause");
	return 0;
}

这样传递相当于是指针,把指针的值传递过去,相当于两个指针直接比大小,在这种情况下会返回地址大的。

有时候并不总是能够写出对所有被实例化的类型都合适的模板,在某些 情况下,通用模板定义对于某个类型可能是完全错误的,或者不能编译,或者做一些错误的事情。

4、特化:通用的处理不了,单独进行处理

(1)必须要现有一个基础的函数模板 

(2)关键字template后面接一对空的尖括

(3)函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参 

(4)函数形参表: 必须要和模板函数的基础参数类型完全相同

(5)函数体

template<class T>
T Max(T left, T right)
{
	return left > right ? left : right;
}
template<>
T Max(T left, T right)
{
 if (strcmp(left, right) > 0)
  return left;
 return right;
 }
int main()
{
	char *p1 = "niha";
	char *p2 = "udk";
	cout << Max(p1, p2) << endl;
	system("pause");
	return 0;
}

注:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类 型完全匹配,如果不匹配,编译器将为实参模板定义中实例化一个实例。


 

猜你喜欢

转载自blog.csdn.net/xuruhua/article/details/80973568