C++ 模板特化与偏特化

1.模板特化

1.1 概述

模板特化(template specialization)不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。

1.2 函数模板特化

函数模板特化指函数模板在模板参数为特定类型下的特定实现。查看以下示例:

#include <iostream>
using namespace std;

template<typename T> T Max(T t1,T t2)
{
	return (t1>t2)?t1:t2;
}

typedef const char* CCP;
template<> CCP Max<CCP>(CCP s1,CCP s2)
{
	return (strcmp(s1,s2)>0)?s1:s2;
}

int main()
{
	//隐式调用实例:int Max<int>(int,int)
	int i=Max(10,5);
	
	//显式调用特化版本:const char* Max<const char*>(const char*,const char*)
	const char* p=Max<const char*>("very","good");
	cout<<"i:"<<i<<endl;
	cout<<"p:"<<p<<endl;
}

程序正常编译运行结果:

i:10
p:very

在函数模板显示特化定义(Explicit Specialization Definition)中,显示关键字template和一对尖括号<>,然后是函数模板特化的定义。该定义指出了模板名、被用来特化模板的模板实参,以及函数参数表和函数体。在上面的程序中,如果不给出函数模板Max< T>在T为const char*时的特化版本,那么在比较两个字符串的大小时,比较的是字符串的起始地址的大小,而不是字符串的内容在字典序中的先后次序。

除了定义函数模板特化版本外,还可以直接给出模板函数在特定类型下的重载形式(普通函数)。使用函数重载可以实现函数模板特化的功能,也可以避免函数模板的特定实例的失效。例如,把上面的模板特化可以改成如下重载函数。

typedef const char* CCP;
CCP Max(CCP s1,CCP s2)
{
	return (strcmp(s1,s2)>0)?s1:s2;
}

程序运行结果和使用函数模板特化相同。但是,使用普通函数重载和使用模板特化还是有不同之处,主要表现在如下两个方面:
(1)如果使用普通重载函数,那么不管是否发生实际的函数调用,都会在目标文件中生成该函数的二进制代码。而如果使用模板的特化版本,除非发生函数调用,否则不会在目标文件中包含特化模板函数的二进制代码。这符合函数模板的“惰性实例化”准则。

(2)如果使用普通重载函数,那么在分离编译模式下,需要在各个源文件中包含重载函数的申明,否则在某些源文件中就会使用模板函数,而不是重载函数。

1.3 类模板特化

类模板特化类似于函数模板的特化,即类模板参数在某种特定类型下的具体实现。考察如下代码:

#include <iostream>
using namespace std;

template<typename T>class A
{
	T num;
public:
	A()
	{
		num=T(6.6);
	}
	void print()
	{
		cout<<"A'num:"<<num<<endl;
	}
};

template<> class A<char*>
{
	char* str;
public:
	A(){
		str="A' special definition ";
	}
	void print(){
		cout<<str<<endl;
	}
};

int main()
{
	A<int> a1;      //显示模板实参的隐式实例化
	a1.print();
	A<char*> a2;    //使用特化的类模板
	A2.print();
}

程序输出结果如下:

A'num:6
A' special definition

2.模板偏特化

2.1 概述

模板偏特化(Template Partitial Specialization)是模板特化的一种特殊情况,指显示指定部分模板参数而非全部模板参数,或者指定模板参数的部分特性分而非全部特性,也称为模板部分特化。与模板偏特化相对的是模板全特化,指对所有的模板参数进行特化。模板全特化与模板偏特化共同组成模板特化。

模板偏特化主要分为两种,一种是指对部分模板参数进行全特化,另一种是对模板参数特性进行特化,包括将模板参数特化为指针、引用或是另外一个模板类。

2.2 函数模板偏特化

假如我们有一个compare函数模板,在比较数值大小时没有问题,如果传入的是数值的地址,我们需要比较两个数值的大小,而非比较传入的地址大小。此时我们需要对compare函数模板进行偏特化。考察如下代码:

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

//函数模板
template<typename T, class N> void compare(T num1, N num2)
{
	cout << "standard function template" << endl;
	if(num1>num2)
		cout << "num1:" << num1 << " > num2:" << num2 <<endl;
	else
		cout << "num1:" << num1 << " <= num2:" << num2 << endl;
}

//对部分模板参数进行特化
template<class N> void compare(int num1, N num2)
{
	cout<< "partitial specialization" <<endl;
	if (num1>num2)
		cout << "num1:" << num1 << " > num2:" << num2 << endl;
	else
		cout << "num1:" << num1 << " <= num2:" << num2 << endl;
}

//将模板参数特化为指针
template<typename T, class N> void compare(T* num1, N* num2)
{
	cout << "new partitial specialization" << endl;
	if (*num1>*num2)
		cout << "num1:" << *num1 << " > num2:" << *num2 << endl;
	else
		cout << "num1:" << *num1 << " <= num2:" << *num2 << endl;
}

//将模板参数特化为另一个模板类
template<typename T, class N> void compare(std::vector<T>& vecLeft, std::vector<T>& vecRight)
{
	cout << "to vector partitial specialization" << endl;
	if (vecLeft.size()>vecRight.size())
		cout << "vecLeft.size()" << vecLeft.size() << " > vecRight.size():" << vecRight.size() << endl;
	else
		cout << "vecLeft.size()" << vecLeft.size() << " <= vecRight.size():" << vecRight.size() << endl;
}

int main()
{
	compare<int,int>(30,31);//调用非特化版本compare<int,int>(int num1, int num2)

	compare(30,'1');		//调用偏特化版本compare<char>(int num1, char num2)

	int a = 30;
	char c = '1';
	compare(&a,&c);		//调用偏特化版本compare<int,char>(int* num1, char* num2)

	vector<int> vecLeft{0};
	vector<int> vecRight{1,2,3};
	compare<int,int>(vecLeft,vecRight);	//调用偏特化版本compare<int,char>(int* num1, char* num2)
}

程序输出结果如下:

standard function template
num1:30 <= num2:31
partitial specialization
num1:30 <= num2:1
new partitial specialization
num1:30 <= num2:1
to vector partitial specialization
vecLeft.size()1 <= vecRight.size():3

2.3 类模板偏特化

类模板的偏特化与函数模板的偏特化类似。考察如下代码:

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

//类模板
template<typename T, class N> class TestClass
{
public:
	static bool comp(T num1, N num2)
	{
		cout <<"standard class template"<< endl;
		return (num1<num2) ? true : false;
	}
};

//对部分模板参数进行特化
template<class N> class TestClass<int, N>
{
public:
	static bool comp(int num1, N num2)
	{
		cout << "partitial specialization" << endl;
		return (num1<num2) ? true : false;
	}
};

//将模板参数特化为指针
template<typename T, class N> class TestClass<T*, N*>
{
public:
	static bool comp(T* num1, N* num2)
	{
		cout << "new partitial specialization" << endl;
		return (*num1<*num2) ? true : false;
	}
};

//将模板参数特化为另一个模板类
template<typename T, class N> class TestClass<vector<T>,vector<N>>
{
public:
	static bool comp(const vector<T>& vecLeft, const vector<N>& vecRight)
	{
		cout << "to vector partitial specialization" << endl;
		return (vecLeft.size()<vecRight.size()) ? true : false;
	}
};

int main()
{
	//调用非特化版本
	cout << TestClass<char, char>::comp('0', '1') << endl;	
	
	//调用部分模板参数特化版本
	cout << TestClass<int,char>::comp(30, '1') << endl;		

	//调用模板参数特化为指针版本
	int a = 30;
	char c = '1';
	cout << TestClass<int*, char*>::comp(&a, &c) << endl;		

	//调用模板参数特化为另一个模板类版本
	vector<int> vecLeft{0};
	vector<int> vecRight{1,2,3};
	cout << TestClass<vector<int>, vector<int>>::comp(vecLeft,vecRight) << endl;	
}

程序输出结果:

standard class template
1
partitial specialization
1
new partitial specialization
1
to vector partitial specialization
1

3.模板类调用优先级

对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序是:全特化类>偏特化类>主版本模板类。这样的优先级顺序对性能也是最好的。

但是模板特化并不只是为了性能优化,更多是为了让模板函数能够正常工作,最典型的例子就是STL中的iterator_traits。algorithm中大多数算法通过iterator对象来处理数据,但是同时允许以指针代替iterator对象,这是为了支持C-Style Array。如果直接操作iterator,那么为了支持指针类型,每个算法函数都需要进行重载,因为指针没有::value_type类型。为了解决这个问题,STL使用了iterator_traits对iterator特性进行封装,并为指针类型做了偏特化处理,算法通过它来操作iterator,不需要知道实际操作的是iterator对象还是指针。

template<typename IteratorClass> class iterator_traits
...
template<typename ValueType> class iterator_traits<ValueType*>
...
template<typename ValueType> class iterator_traits<ValueType const*>
...

后面两是针对指针类型的偏特化,也是偏特化的一种常见形式。


参考文献

[1] 陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.C6.6关于模板特化.P225-P229
[2] C++ Primer中文第5版[M]16.5模板特例化
[3] C++类模板的三种特化,讲得比较全面
[4] C++模板全特化、偏特化
[5] Iterator_traits到底有什么用?

手把手讲授如何搭建成功OpenVINO框架,并且使用预训练模型快速开发超分辨率、道路分割、汽车识别、人脸识别、人体姿态和行人车辆分析。得益于OpenVINO框架的强大能力,这些例子都能够基于CPU达到实时帧率。 课程的亮点在于在调通Demo的基础上更进一步:一是在讲Demo的时候,对相关领域问题进行分析(比如介绍什么是超分辨率,有什么作用)、预训练模型的来龙去脉(来自那篇论文,用什么训练的)、如何去查看不同模型的输入输出参数、如何编写对应的接口参数进行详细讲解;二是基本上对所有的代码进行重构,也就是能够让例子独立出来,并且给出了带有较详细注释的代码;三是注重实际运用,将Demo进一步和实时视频处理框架融合,形成能够独立运行的程序,方便模型落地部署;四是重难点突出、注重总结归纳,对OpenVINO基本框架,特别是能够提高视频处理速度的异步机制和能够直接部署解决实际问题的骨骼模型着重讲解,帮助学习理解;五是整个课程准备精细,每一课都避免千篇一律,前一课有对后一课的预告,后一课有对前一课的难点回顾,避免学习过程中出现突兀;六是在适当的时候拓展衍生,不仅讲OpenVINO解决图像处理问题,而且还补充图像处理的软硬选择、如何在手机上开发图像处理程序等内容,帮助拓展视野,增强对行业现状的了解。 基本提纲: 1、课程综述、环境配置 2、OpenVINO范例-超分辨率(super_resolution_demo) 3、OpenVINO范例-道路分割(segmentation_demo) 4、OpenVINO范例-汽车识别(security_barrier_camera_demo) 5、OpenVINO范例-人脸识别(interactive_face_detection_demo) 6、OpenVINO范例-人体姿态分析(human_pose_estimation_demo) 7、OpenVINO范例-行人车辆分析(pedestrian_tracker_demo) 8、NCS和GOMFCTEMPLATE 9、课程小结,资源分享
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页