STL基础(二)模板特化

模板具体化又被称为特化,有些朋友比较容易把具体化和实例化混淆,这里对两个概念做出对比解释:

  1. 模板实例化:编译器将模板转化为函数或者类型的过程;
  2. 模板特化:修改或覆盖默认的模板实例化过程;

有的时候,特定类型的默认模板可能会有不适用的情况,我们需要为某些特定类型提供特定的实现,这就是模板具体化/特化,特化更能准确描述特定实现的作用,因此我更加倾向用特化这个词。

模板特化分为全特化和偏特化(局部特化),模板函数和模板类又有一些差别,所以我们分开学习。

1、模板函数的特化

1.1、全特化

例如有一个print函数模板,默认打印传入参数的值:

namespace MyTemplate {
	template <typename T>
	void print(T val) {
		std::cout << "default template print: " << val << std::endl;
	}
}

但是我想让传入参数类型为int时打印不同的内容,我们可以对模板做特化(可以理解为特殊化):

template <>
void print<int>(int val) {
	std::cout << "int speclized print: " << val << std::endl;
}

特化写法:template <>表示这是一个函数模板,print后需要加<int>指定模板参数类型。

1.2、调用优先级

如果我们再加上以下代码,这是一个普通函数:

void print(int val) {
	std::cout << "common int print: " << val << std::endl;
}

普通模板、特化模板、普通函数之间的调用顺序是什么呢?来看以下例子:

MyTemplate::print(1);
// 如果普通模板、特化模板、普通函数都有,那么会打印 common int print: 1
// 如果只有普通模板、特化模板,那么会打印 int speclized print: 1
// 如果只有普通模板,则会打印 default template print: 1

所以结论是,如果有多个匹配的函数实现,普通函数将会优先于模板函数,如果没有普通函数,那么编译器将会寻找模板特化函数,如果没有特化函数才会使用模板函数。

调用优先级:普通函数 > 特化函数 > 普通模板

1.3、偏特化

模板函数是没有偏特化的说法的,依旧以print为例:

// 1、2个参数的模板函数
template <typename T1, typename T2>
void print(T1 val1, T2 val2) {
	std::cout << "two arguement defalut print T1: " << val1 << ", T2: " << val2 << std::endl;
}
// 2、全特化模板函数
template<>
void print<int, double>(int val1, double val2) {
	std::cout << "two arguement int print T1: " << val1 << ", double T2: " << val2 << std::endl;
}
// 3、第一个参数为T1,第二个参数为double的模板函数
template <typename T1>
void print(T1 val1, double val2) {
	std::cout << "two arguement print T1: " << val1 << ", double T2: " << val2 << std::endl;
}

全特化的版本print后面会有<>表示具体的模板参数列表,而只有一个参数的版本函数名print之后是没有<>的,所以它不是特化。

进行以下调用:

MyTemplate::print(1, 1);
MyTemplate::print(1, 2.1);
MyTemplate::print<int, double>(1, 2.1);
MyTemplate::print(1.1, 2);
/*
two arguement defalut print T1: 1, T2: 1
two arguement print T1: 1, double T2: 2.1
two arguement int print T1: 1, double T2: 2.1
two arguement defalut print T1: 1.1, T2: 2
*/

在C++中,模板函数的特化版本被视为一个重载的函数版本,编译器选择函数调用时,将优先选择更具体的版本(重载版本)。

例子中print(1, 2.1)会默认使用print(T1 val1, double val2),因为这个版本更为具体,虽然print<int, double>(int val1, double val2)也可以匹配成功,但是它的优先级更低,想要使用这个优先级更低的版本,则需要用显式实例化的方式进行调用。

2、模板类的特化

// 基模板
template <typename T1, typename T2>
class MyClass {
public:
	MyClass(T1 val1, T2 val2) :val1(val1), val2(val2) {}

	void print() {
		std::cout << "default T1 : " << val1 << ", T2 : " << val2 << std::endl;
	}
private:
	T1 val1;
	T2 val2;
};

// 全特化
template <>
class MyClass<int, int> {
public:
	MyClass(int val1, int val2) :val1(val1), val2(val2) {}

	void print() {
		std::cout << "int T1 : " << val1 << ", int T2 : " << val2 << std::endl;
	}
private:
	int val1;
	int val2;
};

// 偏特化
template <typename T1>
class MyClass<T1, double> {
public:
	MyClass(T1 val1, double val2) :val1(val1), val2(val2) {}

	void print();
private:
	T1 val1;
	double val2;
};

template <typename T1>
void MyClass<T1, double>::print() {
	std::cout << "T1 : " << val1 << ", double T2 : " << val2 << std::endl;
}

模板类的全特化和偏特化比较简单一些,template <>template <typename T1>表示一个模板,class MyClass<int, int>class MyClass<T1, double>尖括号表示这是一个模板特化,里面指定了模板类型。

以下是测试用例:

void TestTemplateClassSpecialize() {
	MyTemplate::MyClass<double, char> c1(1.1, 'a');
	MyTemplate::MyClass<int, char> c2(1, 'a');
	MyTemplate::MyClass<int, int> c3(1, 2);
	MyTemplate::MyClass<int, double> c4(1, 2.2);
	c1.print();
	c2.print();
	c3.print();
	c4.print();
}
/*
default T1 : 1.1, T2 : a
default T1 : 1, T2 : a
int T1 : 1, int T2 : 2
T1 : 1, double T2 : 2.2
*/

这里我们要再强调一下,模板类在使用时必须要用显式实例化!

无论是全特化还是偏特化,特化模板的参数数量应该和原模板相同,举个例子:

template <typename T>
class test;

template <>
class test<int>;

//template <>
//class test<>;

//template <>
//class test<int, int>;

test是有一个参数的模板,我们给它做特化时必须传入一个参数,如果我们传入0个参数,或者是传入2个参数都是不可以的。

以上就是模板函数以及模板类特化的基本内容,下一节我们将了解关于模板特化更多的内容。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山渺渺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值