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

相关文章

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

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close