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

相关文章

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

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文件