STL基础(二)模板特化

2024-04-23 17:52
文章标签 基础 模板 特化 stl

本文主要是介绍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个参数都是不可以的。

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

这篇关于STL基础(二)模板特化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/929484

相关文章

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc