C++ 学习系列 -- 模板 template

一  C++ 模板介绍?

  C++ 为什么引入模板?

      我的理解是:

C++ 引入模板的概念,是为了复用重复的代码,当某些代码除了操作的数据类型不同以外,其他逻辑全都相同,此时就适合采用模板的方式。

定义模板类或者模板函数时,只是定义了一个代码的架子,使用时需要配合上实际的数据类型,数据类型可以是基本数据类型也可以是用户自定义的类型。

官方一点的说法:

所谓模板,实际上是建立一个通用函数或类,其类内部的类型和函数的形参类型不具体指定,用一个虚拟的类型来代表。这种通用的方式称为模板。

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

这种模板的设计在许多编程语言中是由类似的,比如 Java 、Scala 中都有模板的概念。

二  class template 类模板

类模板例子:

// complex.h
template<typename T>
class Complex
{
public:Complex(T re = 0, T im = 0):m_re(re), m_im(im){}Complex& operator+=(const Complex& other);T real() const{return m_re;}T imag() const{return m_im;}private:T m_re;T m_im;
};template<typename T>
Complex<T>& Complex<T>::operator+=(const Complex& other)
{this->m_re += other.m_re; // 任意一个类都是自己的 friend class,所以可以使用 private 成员变量this->m_im += other.m_im;return *this;
}// main.cpp
#include"complex.h"int main()
{Complex<double> c1(1, 2);Complex<int>    c2(1.1, 2.2);Complex<float>  c3(1.1, 2.2);return 0;
}

上面是一个复数 Complex模板类的简单实现。

三  function template 函数模板

// complex.h
template<typename T>
class Complex
{
public:Complex(T re = 0, T im = 0):m_re(re), m_im(im){}bool operator<(const Complex& other) const{if(this->m_re < other.m_re){return true;}else if(this->m_re > other.m_re){return false;}else{if(this->m_im < other.m_im){return true;}else{return false;}}}T real() const{return m_re;}T imag() const{return m_im;}private:T m_re;T m_im;
};// main.cpp
#include <iostream>
#include"complex.h"// 模板函数
template<typename T>
const T& myMin(const T& a, const T& b)
{return a < b ? a : b;
}int main()
{int a1 = 11;int b1 = 22;std::cout << myMin(a1, b1) << std::endl;double a2 = 1.1;double b2 = 2.2;std::cout << myMin(a2, b2) << std::endl;Complex<int> c11(11, 2);Complex<int> c12(1, 22);auto cc = myMin(c11, c12);std::cout << cc.real() << ", " << cc.imag() << std::endl;return 0;
}

       以上代码定义了一个模板函数 myMin ;一个模板类 Complex,自定义了一个 operator< 成员函数。

      从代码中我们发现一个问题,模板类要用尖括号指明实际的元素类型,而模板函数并不需要尖括号指明实际的元素类型。这是因为在 C++ 中,模板函数在调用时,可以通过传入的实参推断出形参的类型,因而不需要指明实际的元素类型。

      模板函数 myMin 传入的类型需要有比较的能力,c++ 基本类型天然就具有比较的能力,而用户自定义类型需要实现 operator< 成员函数,才能在编译到 < 那行代码时能够通过,若是自定义类型没有实现 operator< 成员函数时,编译会出现错误。

四  member template 成员模板

成员模板也是一种函数模板,只不过针对的是构造函数的模板。

// base.h
class Base2
{};class Derive2:public Base2
{};class Base3
{};class Derive3:public Base3
{};// my_pair.h
template<typename T1, typename T2>
class MyPair
{
public:typedef T1 firstArgumentType;typedef T1 secondArgumentType;MyPair():first(T1()), second(T2()){}MyPair(const T1& arg1, const T2& arg2):first(arg1), second(arg2){}// 成员模板template<typename U1, typename U2>MyPair(const MyPair<U1, U2>& other):first(other.first),second(other.second){}
public:T1 first;T2 second;
};// main.cpp
#include"my_pair.h"
#include"base.h"int main()
{MyPair<Base2, Base3> pair1;MyPair<Derive2, Derive3> pair2;MyPair<Base2, Base3> pair3(pair2);return 0;
}

五  模板特化(specialization)与偏特化(partial specialization)

1. 模板特化 (specialization)

      模板特化指得是将模板类或者模板函数特化到某些特定的指定类型,使得在编译器编译时,实际编译执行的代码是特化后的版本。

 

// my_hash.h
#include<iostream>
#include<iostream>template <class T>
struct my_hash
{T operator()(T x){std::cout << "template <class T> struct my_hash" << std::endl;return x;}
};// 以下为三个特化版本
template<>
struct my_hash<int>
{int operator()(int x){std::cout << "template <> struct my_hash<int>" << std::endl;return x;}
};template<>
struct my_hash<char>
{char operator()(char x){std::cout << "template <> struct my_hash<char>" << std::endl;return x;}
};template<>
struct my_hash<long>
{long operator()(long x){std::cout << "template <> struct my_hash<long>" << std::endl;return x;}
};
// main.cpp#include"my_hash.h"int main()
{std::cout << my_hash<std::string>()("abcd") << std::endl;std::cout << my_hash<char>()('a') << std::endl;std::cout << my_hash<int>()(66) << std::endl;std::cout << my_hash<long>()(6666) << std::endl;return 0;
}

 输出:

2. 模板偏特化(partial specialization)

2.1  个数偏特化

      模板的个数变化时的特化版本

// my_vector.h
#include<iostream>template<typename T, typename Alloc>
class my_vector
{
public:my_vector(){std::cout << "template<typename T, typename Alloc> class my_vector" << std::endl;}
};template<typename Alloc>
class my_vector<bool, Alloc>
{
public:my_vector(){std::cout << "template<typename Alloc> class my_vector<bool, Alloc>" << std::endl;}
};// main.cpp
#include"my_vector.h"int main()
{my_vector<std::string, std::allocator<std::string>>  vec1;my_vector<bool, std::allocator<std::string>>  vec2;return 0;
}

输出:

2.2 范围偏特化

      通过特化模板类型的指针来实现指针的特化

// e.h
#include<iostream>template<typename T>
class E
{
public:E(){std::cout << "template<typename T> class E" << std::endl;}
};template<typename T>
class E<T*>
{
public:E(){std::cout << "template<typename T> class E<T*>" << std::endl;}
};// main.cpp
#include"e.h"int main()
{E<std::string> e1;E<std::string*> e2;return 0;
}

输出:

六  模板模板参数(template template parameter)

模板模板参数其实就是模板的其中参数又是另外一个模板参数,示例代码如下:

XCLS 类 的第一个模板参数是 T ,第二个模板参数是一个模板类 Container,我们可以定义一个 XX 模板类来组合使用 XCLS ,将 XX 传入即可;

也可以使用容器类 list  传入,但是如果直接传入编译是会出错的,比如下面

   XCLS<std::string, std::list> xcls3; 编译不过

  编译不过的原因是,std::list 实际上是两个模板参数,编译器检查时发现少给了一个模板参数 _Alloc ,所以编译不过。 

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >class list;

可以通过如下定义 Lst 来将模板类 list 的模板参数限定为 一个

template<typename T>
using Lst = std::list<T, std::allocator<T>>;
// xcls.h
#include<iostream>template<typename T, template<typename U> class Container>
class XCLS
{
public:XCLS(){std::cout << "template<typename T, template<typename U> class Container> class XCLS" << std::endl;}
private:Container<T> c;
};template<typename T>
class XX
{
public:XX(){std::cout << "template<typename T> class XX" << std::endl;}
};// main.cpp
#include"xcls.h"int main()
{  // 模板模板的参数使用XCLS<std::string, XX> xcls1;XCLS<std::string, Lst> xcls2;// XCLS<std::string, std::list> xcls3; 编译不过return 0;
}

输出:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/225052.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑豹程序员-axios+springmvc传递数组

问题 奇怪的现象&#xff0c;axios在往后台传递数组时&#xff0c;springmvc竟然接收不到 解决 尝试多次无果&#xff0c;突然看一篇文章写vue中的数组不是真正的数组需要强转转化JSON.stringify 将信将疑下测试了一把&#xff0c;还真的传递成功了。 不光要JSON.stringify…

Github 2023-12-15 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-15统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量TypeScript项目3非开发语言项目3JavaScript项目1Python项目1Rust项目1PHP项目1 基于项目的学习 创建周期&am…

微服务组件Sentinel的学习(3)

Sentinel 隔离和降级Feign整合Sentinel线程隔离熔断降级熔断策略 授权规则&#xff1a;自定义异常 隔离和降级 虽然限流可以尽量避免因高并发而引起的服务故障&#xff0c;但服务还会因为其它原因而故障。而要将这些故障控制在一定范用避免雪崩&#xff0c;就要靠线程隔离(舱壁…

Dockerfile创建镜像--LNMP+wordpress

实验准备&#xff1a; nginx&#xff1a;172.111.0.10 docker-nginx mysql&#xff1a;172.111.0.20 docker-mysql php&#xff1a;172.111.0.30 docker-php 自定义网段&#xff1a;172.111.0.0/16mkdir nginx mysql php mv nginx-1.22.0.tar.gz wordpress-6.4.2-zh_CN.ta…

Polkadot 品牌焕新提案:重返前卫,市场营销的创新愿景

波卡的品牌形象和营销策略也许将迎来新变化。长久以来一些社区成员批评道&#xff0c;波卡的形象过于保守、太企业化&#xff0c;缺乏 Crypto 行业应有的先锋气质。 在前阵子的 Parity “去中心化” 变革中&#xff0c;Parity 的营销团队经历了大幅的变动&#xff0c;随后建立…

UGUI 鼠标悬浮UI出现弹框,鼠标在图片边缘出现闪烁

1、背景&#xff1a;鼠标悬浮在UI上出现提示框 public class SpecialParam_list : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {public void OnPointerEnter(PointerEventData eventData){TipBox.Instance.ShowBox(Input.mousePosition, value);}public void …

如何实现免费的文档翻译

文中有彩蛋&#xff0c;请一定要看完。 目录 文中有彩蛋&#xff0c;请一定要看完。 一、问题的提出 二、文档翻译现状 三、如何免费海量文档翻译 1. 采用CAT工具机器翻译API法 2. 采用小牛文档翻译 四、学后反思 一、问题的提出 随着互联网和人工智能技术的飞速发展&…

Kubernetes 容器编排(2)

可视化部署 官方Dashboard 部署Dashboard # kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml # kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard # 注意将 type: ClusterIP 改为 type: NodePo…

案例064:基于微信小程序的考研论坛设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

php入门、安装wampserver教程

php声称是全世界最好的语言&#xff0c;今天这篇文章就带大家入门学习php&#xff0c;php和python、javasript一样&#xff0c;是一种弱类型的脚本语言。 一、php开发环境搭建 作为初学者&#xff0c;学习php建议安装wampserver&#xff0c;wampserver是包含了apache、php和mys…

MFC逆向之CrackMe Level3 过反调试 + 写注册机

今天我来分享一下,过反调试的方法以及使用IDA还原代码 写注册机的过程 由于内容太多,我准备分为两个帖子写,这个帖子主要是写IDA还原代码,下一个帖子是写反调试的分析以及过反调试和异常 这个CrackMe Level3是一个朋友发我的,我也不知道他在哪里弄的,我感觉挺好玩的,对反调试…

VMP泄露编译的一些注意事项

VMP编译教程 鉴于VMP已经在GitHub上被大佬强制开源&#xff0c;特此出一期编译教程。各位熟悉的可以略过&#xff0c;不熟悉的可以参考一下。 环境&#xff08;软件&#xff09; Visual Studio 2015 - 2022 &#xff08;建议使用VS2019&#xff0c;Qt插件只有这个版本及以上…

vscode的文件和文件夹的警告标志如何消去

由于平时用vscode写一些java的小demo, 但是这个vscode的警告和错误管理很奇怪, 这个警告信息会显示在这个侧边的文件和文件夹中, 我上网上找能不能把这个给去掉的办法, 找了半天没找到。 于是我就自己去查了一下这个vscode的设置, 真让我找到了这方面的开关, 把下面的这个关闭…

Linux学习笔记-Ubuntu下ssh服务器连接异常Connection reset

文章目录 一、问题问题现象1.1 连接重置无法访问的的问题1.2 查看服务器连接状态1.3 使用调试模式查看的信息 二、临时解决方法三、从根源解决问题3.1 问题分析3.2 服务器的ssh日志3.3 修改ssh配置禁止root登录3.4 配置允许所有ip访问3.5 修改认证方法 角色&#xff1a;百世经纶…

selenium+xpath爬取二手房标题

贝壳找房标题爬取需要注意的是&#xff0c;在页面中间有一个小广告 而他就在ul的li下面&#xff0c;当我们进行title所以输出时&#xff0c;会报错。 所以在进行页面解析之前必须把广告叉掉&#xff0c;不然也把广告那一部分的li给爬取下来了 所以&#xff0c;我们&#xff0…

软件测试职业规划

软件测试人员的发展误区【4】 公司开发的产品专业性较强&#xff0c;软件测试人员需要有很强的专业知识&#xff0c;现在软件测试人员发展出现了一种测试管理者不愿意看到的景象&#xff1a; 1、开发技术较强的软件测试人员转向了软件开发(非测试工具开发)&#xff1b; 2、业务…

蓝桥杯专题-真题版含答案-【古代赌局】【古堡算式】【微生物增殖】【密码发生器】

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

C++报错:error C2238: 意外的标记位于“;”之前

报错信息如下&#xff1a; 编译遇见这样的错误信息主要有一下几种&#xff1a; 情况一&#xff1a; 多数情况下出现这种问题的原因是因为头文件重复包含&#xff1a;即头文件A包含了B&#xff0c;头文件B又包含了A&#xff0c;导致编译器在加载头文件时陷入死循环。 解决办法…

力扣题:数字与字符串间转换-12.16

力扣题-12.16 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;640. 求解方程 解题思想&#xff1a;首先将方程按照“”进行划分&#xff0c;然后分别记录x的因数和常数项&#xff0c;最后进行返回的判断即可 class Solution(object):def solveEqu…

万户 OA OfficeServer.jsp 任意文件上传漏洞复现

0x01 产品简介 万户OA是面向政府组织及企事业单位的FlexOffice自主安全协同办公平台。 0x02 漏洞概述 万户OA OfficeServer.jsp接口存在任意文件上传漏洞,攻击者可通过该漏洞上传任意文件从而控制整个服务器。 0x03 复现环境 FOFA: (banner="OASESSIONID" &a…