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

相关文章

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4

Spring Boot集成Logback终极指南之从基础到高级配置实战指南

《SpringBoot集成Logback终极指南之从基础到高级配置实战指南》Logback是一个可靠、通用且快速的Java日志框架,作为Log4j的继承者,由Log4j创始人设计,:本文主要介绍... 目录一、Logback简介与Spring Boot集成基础1.1 Logback是什么?1.2 Sprin

MySQL复合查询从基础到多表关联与高级技巧全解析

《MySQL复合查询从基础到多表关联与高级技巧全解析》本文主要讲解了在MySQL中的复合查询,下面是关于本文章所需要数据的建表语句,感兴趣的朋友跟随小编一起看看吧... 目录前言:1.基本查询回顾:1.1.查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J1.2.按照部门

Python中Flask模板的使用与高级技巧详解

《Python中Flask模板的使用与高级技巧详解》在Web开发中,直接将HTML代码写在Python文件中会导致诸多问题,Flask内置了Jinja2模板引擎,完美解决了这些问题,下面我们就来看看F... 目录一、模板渲染基础1.1 为什么需要模板引擎1.2 第一个模板渲染示例1.3 模板渲染原理二、模板