本文主要是介绍STL基础(二)模板特化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
模板具体化又被称为特化,有些朋友比较容易把具体化和实例化混淆,这里对两个概念做出对比解释:
模板实例化
:编译器将模板转化为函数或者类型的过程;模板特化
:修改或覆盖默认的模板实例化过程;
有的时候,特定类型的默认模板可能会有不适用的情况,我们需要为某些特定类型提供特定的实现,这就是模板具体化/特化,特化
更能准确描述特定实现的作用,因此我更加倾向用特化这个词。
模板特化分为全特化和偏特化(局部特化),模板函数和模板类又有一些差别,所以我们分开学习。
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个参数都是不可以的。
以上就是模板函数以及模板类特化的基本内容,下一节我们将了解关于模板特化更多的内容。
这篇关于STL基础(二)模板特化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!