15.6 模板全特化与偏特化(局部特化)

泛化:模板,可以随便指定类型。
特化: 对特殊的类型(类型模板参数)进行特殊的对待,给他开小灶,给它写适合它的专用代码。

一: 类模板特化

<1>类模板全特化
1>常规全特化
必须先有泛化版本,才能存在特化版本。只要涉及特化,一定先存在泛化。
特化版本代码编译器会优先选择。
TC<char, int> tcchar;
tcchar.functest(); //泛化版本

TC<int, int> tcdint;
tcdint.functest(); //int, int的特化版本
//这种特化版本可以任意多;我们目前是写了两个。

2>特化成员函数而不是模板
TC<double, double> tdbldbl; //泛化版本对象, 调用的是泛化版本的构造函数。
tdbldbl.functest(); //因为我们特化了double, double类型的functest函数,所以这里调用的是特化的functest()函数。

template<typename T, typename U>
struct TC  //泛化的TC类模板
{
	TC()
	{
		cout << "TC泛化版本构造函数" << endl;
	}

	void functest()
	{
		cout << "TC泛化版本" << endl;
	}
};

//当T和U这两个类型模板参数都为int类型时,我们希望做一个特化版本
//全特化:就是所有类型模板参数(这里T和U),都得用具体的类型代表。
template<>  //全特化: 所有类型模板参数都用具体类型代表,所以这里的template后边的<>里为空
struct TC<int, int>  //上边的T绑定到这里的第一个int,上边的U绑定到这里的第二个int
{
	TC()
	{
		cout << "TC<int, int>的特化版本构造函数" << endl;
	}

	//在这里可以对该特化版本做单独处理
	void functest()
	{
		cout << "TC<int, int>的特化版本" << endl;
	}
};

template<>  //全特化: 所有类型模板参数都用具体类型代表,所以这里的template后边的<>里为空
struct TC<double, int>  //上边的T绑定到这里的第一个double,上边的U绑定到这里的第二个int
{
	//在这里可以对该特化版本做单独处理
	void functest()
	{
		cout << "TC<double, int>的特化版本" << endl;
	}
};

template<>  //全特化
void TC<double, double>::functest()
{
	cout << "double, double的functest()的特化版本" << endl;
}

int main()
{
	TC<char, int> tcchar;
	tcchar.functest();  //泛化版本

	TC<int, int> tcdint;
	tcdint.functest();  //int, int的特化版本

	TC<double, double> tdbldbl;  //泛化版本对象, 调用的是泛化版本的构造函数。
	tdbldbl.functest();  //因为我们特化了double, double类型的functest函数,所以这里调用的是特化的functest()函数。
	return 0;
}

<2>类模板偏特化 (局部特化)
偏特化从两方面说起:一个是从模板参数数量上,一个是从模板参数范围上

1>模板参数数量
TC<double, int, double> tcdi;
tcdi.functest(); //泛化版本
TC<int, int, double> teii;
teii.functest(); //偏特化int, U, double版本

//泛化版本
template <typename T, typename U, typename W>  //带三个类型模板参数
class TCP
{
public:
	TCP()
	{
		cout << "TCP泛化版本构造函数" << endl;
	}
	void functest()
	{
		cout << "TCP泛化版本" << endl;
	}
};

//从参数数量上进行偏特化,我们现在绑2个类型模板参数。留1个模板类型参数。
template<typename U>  //因为另外两个被我绑定到具体类型,所以这里只剩下一个U类型模板参数了。
struct TCP<int, U, double>  //大家注意,这里可以跳着来
{
	TCP()
	{
		cout << "TCP<int, U, double>偏特化版本构造函数" << endl;
	}
	void functest()
	{
		cout << "TCP<int, U, double>偏特化版本" << endl;
	}
};

int main()
{
	TCP<double, int, double> tcpdi;  //TCP泛化版本构造函数
	tcpdi.functest();  //TCP泛化版本

	TCP<int, int, double> tcpii;  //TCP<int, int, double>偏特化版本构造函数
	tcpii.functest();  //TCP<int, int, double>偏特化版本
	return 0;
}

2>模板参数范围上:int, const int(比int小),
原来T,现在T* (从任意类型T索晓伟指针类型T*)
原来是T,现在T&左值引用,或者现在是T&&(右值引用),都叫范围缩小。
TC td;
td.functest(); //泛化版本

TC<double*> tpd;
tpd.functest(); //T*特化版本

TC<const double*> tpd2;
tpd2.functest(); //T*特化版本

TC td3;
td3.functest(); //const T特化版本

TC<int&> tcyi;
tcyi.functest(); //T&左值引用特化版本

TC<int&&> tcyii;
tcyii.functest(); //T&&右值引用特化版本

局部特化,特化完了之后它本质上还是个模板。全特化,特化完了等于一个具体的类了。

//泛化版本
template <typename T>
struct TC
{
	void functest()
	{
		cout << "泛化版本" << endl;
	}
};

//模板参数范围上的特化版本
template <typename T>
struct TC<const T>
{
	void functest()
	{
		cout << "const T特化版本" << endl;
	}
};

template <typename T>
struct TC<T*>  //T*的特化版本,告诉编译器,如果使用指针,就调用这个版本。
{
	void functest()
	{
		cout << "T*特化版本" << endl;
	}
};

template <typename T>
struct TC<T&>  //左值引用的特化版本
{
	void functest()
	{
		cout << "T&特化版本" << endl;
	}
};

template <typename T>
struct TC<T&&>  //右值引用的特化版本
{
	void functest()
	{
		cout << "T&&特化版本" << endl;
	}
};

int main()
{
	TC<double> td;
	td.functest();  //泛化版本

	TC<double*> tpd;
	tpd.functest();  //T*特化版本

	TC<const double*> tpd2;
	tpd2.functest();  //T*特化版本

	TC<const double> td3;
	td3.functest();  //const T特化版本

	TC<int&> tcyi;
	tcyi.functest();  //T&左值引用特化版本

	TC<int&&> tcyii;
	tcyii.functest();  //T&&右值引用特化版本
	return 0;
}

二:函数模板特化

<1>函数模板全特化
const char* p = “I love China!”;
int i = 12;
tfunc(p, i); //T = const char * , tmprv = const char * & , U = int , tmprv2 = int&

int i = 12;
double db = 15.8f;
tfunc(i, db);

全特化函数模板实际上等价于实例化一个函数模板,并不是等价于一个函数重载

void tfunc<int, double>(int& tmprv, double& tmprv2){} //全特化,等价于实例化一个函数模板

void tfunc(int& tmprv, double& tmprv2){} //重载函数应该长这样

编译器选择最最合适的:普通优先,特化版本,泛化版本;(优先级顺序排列)

如果你传递个字符串给函数模板
函数模板的特化版本中,如果有数组类型模板参数, 指针类型模板参数。
编译器会认为数组类型模板参数比指针类型模板参数更合适。所以编译器会为你选择数组类型的模板参数的特化版本。

<2>函数模板偏特化
函数模板不能偏特化

//函数模板泛化版本
template<typename T, typename U>
void tfunc(T& tmprv, U& tmprv2)
{
	cout << "tfunc泛化版本" << endl;
	cout << tmprv << endl;
	cout << tmprv2 << endl;
}

函数模板偏特化  
//template<typename U>
//void tfunc<double, U>(double& tmprv, U& tmprv2)  //“tfunc”: 非法使用显式模板参数
//{
//	//......
//}

//全特化版本,T = int, U = double
template<>
void tfunc(int& tmprv, double& tmprv2)
{
	cout << "-------------begin---------------" << endl;
	cout << "tfunc全特化版本int, double" << endl;
	cout << tmprv << endl;
	cout << tmprv2 << endl;
	cout << "-------------end---------------" << endl;
}

//重载函数
void tfunc(int& tmprv, double& tmprv2)
{
	cout << "tfunc重载函数(int, double)" << endl;
}


int main()
{
	const char* p = "I love China!";
	int i1 = 12;
	tfunc(p, i1);  //T = const char * , tmprv = const char * & , U = int , tmprv2 = int&

	int i2 = 12;
	double db = 15.8f;
	tfunc(i2, db);
	return 0;
}

三: 模板特化版本放置位置建议

模板定义、实现都放在一个 .h中;
模板的特化版本和模板的泛化版本都应该放在同一个 .h 文件中。
.h 文件中前面放泛化版本,后面放特化版本。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值