C++模板进阶

Posted  落禅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++模板进阶相关的知识,希望对你有一定的参考价值。

C++模板进阶

# C++模板初阶

1.非类型模板参数

template<class T, size_t N>
class Array
{
    private:
	T arr[N];
};

模板参数分为类型参数和非类型参数,如上述所述代码就是非类型模板参数

非类型参数:出现在模板的参数类表中,更在class或者typename之类的参数类型名称

非类型参数就是作为一个类(函数)的参数,在模板中该参数可以被当作常量来使用

比如库里面的Array就是使用非类型模板参数来完成的

当然,非类型模板也可以有默认的缺省值

#include<iostream>
using namespace std;
namespace hello
{
	template<class T, size_t N=100>
	class Array
	{
	private:
		T arr[N];
	};
}
int main()
{
	hello::Array<int>arr;
	
	return 0;
}

注意:

1.浮点数,类对象以及字符串是不容许作为非类型模板参数的

2.非类型的模板参数必须在编译器就能确认结果

2.模板的特化

在某些情况下,使用模板可能会出现一些错误的结果,例如下面对两个字符串的比较

//例如利用模板来比较两个字符串是否相等
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
	return left == right;
}
int main()
{
	int a = 10;
	int b = 10;
	cout << Is_Same<int>(a,b) << endl;//结果为1
	char str1[] = "hello";
	char str2[] = "hello";
	cout << Is_Same(str1, str2) << endl;//结果为0
	return 0;
}

上面程序运行时会分别打印1,0,这说明我们在比较str1和str2这两个字符串时出现了错误,为什么会出现str1和str2不相等的情况呢?

数组名是数组首元素的地址,是一个指针,在模板参数进行实例化的时候,模板参数T会被替换成一个char*类型的指针,而这两个字符数组都是位于栈区的,二者地址不相同,所以会返回false

而这种特殊情况就需要对模板进行特化,即:在原模版的基础上,针对特殊类型所进行特殊化的实现方式,分为函数模板特化和类模板特化

1.函数模板特化

步骤:

1.首先必须现有一个基础的函数模板

2.template后面接一对尖括号<>

3.函数名后面跟一对健康括号,尖括号内指定需要特化的类型

4.函数形参列表:必须要和函数模板的基础参数类型完全相同,如果编译器不同可能报一些奇怪的错误

下面,我们对上面哪个求两个字符串是否相等做一些改变:

//方法一:直接重新定义一个同名函数,里面参数写出char*,当我们执行Is_Same函数时,编译器首先会在非模板函数中查找是否有能够进行匹配的,如果参数匹配直接执行该非模板函数,不匹配则在模板函数中寻找
#include<iostream>
using namespace std;
template<class T>
bool Is_Same(T& left, T& right)
{
	return left == right;
}

//重新定义一个函数判断两个字符串是否相等
bool Is_Same(char* left,char*  right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}
int main()
{
	int a = 10;
	int b = 10;
	cout << Is_Same<int>(a,b) << endl;
	char str1[] = "hello";
	char str2[] = "hello";
	cout << Is_Same(str1, str2) << endl;
	return 0;
}

//方法二:按照上面所说的函数模板的特化重新写一个函数模板的特化
template<>
bool Is_Same<const char*const>(const char* const &left, const char* const &right)
{
	if (strcmp(left, right) == 0)
		return true;
	return false;
}

2.类模板的特化

//最简单的类模板
template<class T1, class T2>
class A
{
private:
	T1 a;
	T2 b;
};

1.全特化

全特化即将模板参数列表的所有参数都进行确定

//例如:
//在A<int,int>下我们可以执行自己想要做的是
//在A<int,double>下完成另外一件事
#include<iostream>
using namespace std;
template<class T1,class T2>
class A
{
public:
	A()
	{
		cout << "A<T1,T2>" << endl;
	}
};
template<>
class A<int, int>
{
public:
	A()
	{
		cout << "A<int ,int>" << endl;
	}
};
template<>
class A<int, double>
{
public:
	A()
	{
		cout << "A<int,double>" << endl;
	}
};

int main()
{
	A<int, int>a;
	A<int, double>b;
	A<char, int>c;
	return 0;
}

2.偏特化

即给定类模板一般参数

1.部分特化:将模板参数列表中的一部分参数进行特化

class A<T1,int>
{
public:
	A()
	{
		cout << "A<T1,int>" << endl;
	}
};

2.参数进一步限制

偏特化不仅仅使之特化部分参数,而是正对模板参数更近一步的条件限制所设计出来的一个特化版本

//两个参数偏特化为指针类型
template<class T1,class T2>
class A<T1*,T2*>
{
public:
	A()
	{
		cout << "A<T1*,T2*>" << endl;
	}
};
//两个参数偏特化为引用类型
template<class T1, class T2>
class A<T1& ,T2&>
{
public:
	A()
	{
		cout << "A<T1&,T2&>" << endl;
	}
};
//一个参数偏特化为引用,另外一个参数偏特化为指针
template<class T1,class T2>
class A<T1&,T2*>
{
public:
	A()
	{
		cout << "A<T1&,T2*>" << endl;
	}
};

3.模板的分离编译

1.模板的分离编译即test.h中写类模板的声明,test.cpp中写类模板的实现,main.cpp中写主要函数功能

-------test.h---------
模板的定义


--------test.cpp-------
模板的实现



--------main.cpp--------
模板的调用

2.在书写模板类时不能采取这种方式,会出现链接错误

一个c/c++程序的执行大概遵守以上图片的过程,经过预处理,编译,汇编,链接,最终形成可执行程序

test.h中放函数的声明,test.cpp中放函数的实现,main.c中调用函数,经过预处理,编译,汇编之后会生成test.o和main.o,当调用这个模板函数时,发现当前只有函数的声明,则回去test.o的符号表中去找,因为模板只有在实例化时才能生成对应的代码,符号表里面没有该模板函数的地址,就会出现链接错误

3.解决方法

1.将声明和定义放到文件"xxx.hpp"里面或者xxx.h中

2.模板定义的位置显式实例化

ps:推荐直接声明与定义直接放在.h文件中

欢迎大家的观看,期待下次重逢 To Be Continued…

以上是关于C++模板进阶的主要内容,如果未能解决你的问题,请参考以下文章

C++初阶第十三篇—模板进阶(非类型模板参数+模板特化+模板的分离编译)

C++之模板进阶

C++之模板进阶

C++——模板进阶

C++模板进阶

C++模板进阶