【C++技能树】一文看懂模板匹配

news2024/12/28 21:48:05

在这里插入图片描述
Halo,这里是Ppeua。平时主要更新C++,数据结构算法,Linux与ROS…感兴趣就关注我bua!

文章目录

  • 0.泛型编程
  • 1.模板
  • 2 函数模板:
    • 2.1函数模板的特化
  • 3. 类模板
    • 3.1 非类型模板参数
    • 3.2 类的模板刻画
      • 3.2.1 全特化
      • 3.2.2 偏特化

在这里插入图片描述

0.泛型编程

假设有下面这个场景:

需要获得一个swap函数可以交换整形.

void swap(int &a,int &b)
{
    int tmp=a;
    a=b;
    b=tmp;
}

如果这时候我有需要一个能够交换浮点型与浮点型、自定义类型与自定义类型…等函数呢

这些需求有一个共同点,他们的整体逻辑是一样的,但仅因为类型的改变就需要重构一份代码.这显然是效率低下不可取的.

所以C++引入了一个新的特性:泛型编程.

可以想象为:钢厂里需要制造钢具,他们的模子是一样的,但是颜色不同.所以我们可以 根据需求将相同的模子刷上不同的颜色

这里的模子就是代码逻辑,颜色就是类型.

1.模板

模板提供了多样的类型,这是泛型编程的基础.

template<class T>
// template<typename T>
code......

模板基础语法如上,其中class与typename没有什么大的差别.T为自定义名称,通常习惯为T(就如常量大写一样)

该模板的作用域是紧贴的那一段code(class或者function)

经过实例化之后,每一个函数都是不同的函数,每一个类也是不同的类,即使他们逻辑相同

将通过两个方面来解释模板:

  1. 函数模板

  2. 类模板

    a132a655bef4a4d4e93ec1857d03739

2 函数模板:

还是上面swap的例子.利用模板的方法是这么写:

template<class t>
void Swap(t t1,t t2)
{
    t tmp;
    tmp=t1;
    t1=t2;
    t2=tmp;
    
}
int main()
{
    int a=0;
    int b=2;
    Swap(a,b); 
}

此时就可以通过int去交换.

image-20230809160755635

可以直接将t看做成一个类如int double…等.所以正常写法上出现类型的地方都可以用t去代替

上文为隐式实例化:就是由编译器去自动推导需要刻画一个什么样的模板

但我们也可以自己去告诉编译器我们需要一个什么样的模板.也就是显式实例化

Swap<int>(a,b);

通常情况下,我们用隐式实例化即可.但如果有下面这样的函数,我们就需要显式实例化

T* Alloc(int n=10)
{
    return new T[n];
}
int main()
{
    auto array=Alloc<int>(2); 
}

该函数作用为创建一段默认为10大小的数组空间并返回.

这时候如果我们不告诉编译器我们需要什么样的数组,它又怎么会知道呢?

这就是显式实例化的意义所在

  1. 当调用时有一个函数与模板函数同名时,会优先去查找这个函数是否满足要求.若满足要求则会优先调用已有的函数,而不是用模板去刻画一个函数.

    int Add(int left, int right)
    {
     return left + right;
    }
    
    template<class T>
    T Add(T left, T right)
    {
     return left + right;
    }
    void Test()
    {
     Add(1, 2); 
     Add<int>(1, 2); 
    }
    

    输出结果为

image-20230808213538903

  1. 函数模板不可自动类型转换,但普通函数可以

这里可以理解为:函数模板会去适配参数类型,而普通函数需要参数类型去适配普通函数

2.1函数模板的特化

如果我想要设计一个函数根据不同的传入对象做不同的事情.就需要用到函数的特化这一概念

函数特化的步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
template<class T>
bool Less(T left,T right)
{
    return left<right;
}

template <>
bool Less<int*>(int *left,int *right)
{
    return *left<*right;
}

但这样写还不如直接函数重载,所以实用性不大

3. 类模板

这里放上之前提到过的Vector部分源代码演示,想要进一步了解vector的可以看这篇文章:vector的理解与使用

#pragma once
#include<iostream>
namespace H
{
    template<class T>
    class vector {
    public:
        typedef T* iterator;
        typedef const T* const_iterator;
        vector() 
        {
        }
        vector(size_t n, const T& val = T())
        {
            resize(n, val);
        }
        vector(int n, const T& val = T())
        {
            resize(n, val);
        }
        template<class InputIterator>
        vector(InputIterator begin, InputIterator end)
        {
            while (begin != end)
            {
                push_back(*begin);
                begin++;
            }
        }
        vector(const vector<T>& v)
        {
            _start = new T[v.capacity()];
            for (size_t i = 0; i < v.size(); i++)
            {
                _start[i] = v._start[i];
            }
            //_finish = v._finish; 只是令地址相等
            _finish = _start + v.size();
            _endofstorage = _start + v.capacity();
        }
        vector<T>& operator= (const vector<T>& v)
        {
            swap(v);
            return *this;
        }
        ~vector()
        {
            if (_start)
            {
                delete[]_start;
                _start = _finish = _endofstorage = nullptr;
            }
        }
        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator begin()const
        {
            return _start;
        }
        const_iterator end()const
        {
            return _finish;
        }
        size_t capacity()const
        {
            return _endofstorage - _start;
        }
        size_t size()const
        {
            return _finish - _start;
        }
        void resize(size_t n, const T& val)
        {
            if (n < size())
            {
                _finish = _start + n;
            }
            else
            {
                reserve(n);
                while (_finish != _start + n)
                {
                    *_finish = val;
                    _finish++;
                }
            }
        }
        void reserve(size_t n)
        {
            if (n > capacity())
            {
                size_t sz = size();
                T* tmp = new T[n];
                if (_start)
                {
                    memcpy(tmp, _start, sizeof(T) * size());
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;
                _endofstorage = _start + n;
            }
        }
        void push_back(const T& x)
        {
            if (_finish == _endofstorage)
            {
                reserve(capacity() == 0 ? 1 : capacity() * 2);
            }
            *_finish = x;
            _finish++;
        }
        void swap(vector<T>v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endofstorage, v._endofstorage);
        }
        iterator insert(iterator pos, const T& val)
        {
            assert(pos < _finish&& pos >= _start);
            if (_finish == _endofstorage)
            {
                size_t newpos = pos - _start;
                reserve(capacity() == 0 ? 1 : capacity() * 2);
                pos = _start + newpos;
            }
            iterator end = _finish - 1;
            while (end >= pos)
            {
                *(end + 1) = end;
                end--;
            }
            *pos = val;
            ++_finish;
            return pos;

        }
        iterator erase(iterator pos)
        {
            assert(pos < _finish&& pos >= _start);
            iterator it = pos + 1;
            while (it != _finish)
            {
                *(it - 1) = *it;
                it++;
            }
            _finish;
            return pos;
        }
        T& operator[](size_t pos)
        {
            assert(pos < size());

            return _start[pos];
        }

        const T& operator[](size_t pos) const
        {
            assert(pos < size());

            return _start[pos];
        }
        void pop_back()
        {
            erase(--end());
        }
    private:
        iterator _start = nullptr;
        iterator _finish = nullptr;
        iterator _endofstorage = nullptr;


    };

}

这就是类代码的实例化,与函数模板的实例化大差不差,但是调用的时候需要显示实例化,这也很好理解,你不传参数他并不知道需要创造出一个什么样的类.

vector<int>v;

用到模板时:类名不再是类型,类型为vector,若实例化之后T为具体的类型

因为存在静态成员变量,所以使用泛型编程的时候,遇到使用模板参数去申明变量的时候.前面需要先申明该参数为类型而不是变量

template <class Container>
void Print(Container&v)
{
    //成分不明确 静态类型对象与类型 
    //typename 明确告诉编译器 这里是类型的模板实例化
    typename Container::const_iterator it=v.begin();
    //auto it=v.begin() auto为类型所以不需要加typename
    //vector<int>::const_iterator
    while(it!=v.end())
    {
        cout<<*it<<endl;
        it++;
    }
    cout<<endl;
}

例如假设Data类中有一个st静态成员变量.进行赋值的时候是

Data::st=10;

而假设Data类中有一个st类型需要定义时是这样的

Data::st stt=10;

这两明显意义不同但编译阶段容易被编译器混淆.所以需要加上typename关键字

typename Data::st stt=10;

上面的Data在泛型编程中,被替换为模板关键字,用来表示类的类型.

3.1 非类型模板参数

我们有时候想要动态的创建一个栈(若这个栈不扩容),所以需要我们在实例化的时候动态的给定一个大小.这时候就需要一个非类型模板参数来传递这个数据.

template <class T,size_t N=10>
class Stack{
public:
    
    T a[N];
    int _top;
    int capacity=N;
};

传入参数通过这样传入:

Stack<int,20> st20;

但注意,非类型模板参数只支持整形

这也很好理解,难道创建数组空间的大小可以为浮点型嘛.

3.2 类的模板刻画

与函数相同,刻画时仍然需要有原始类.

template<class T1,class T2>
class Date{
public:
    void print()
    {
        cout<<"Date<T1,T2>"<<endl;
    }
private:
    T1 a1=0;
    T2 a2=0;
};

3.2.1 全特化

顾名思义:是将全部的的函数参数都进行特化

template<>
class Date<int,double>
{
public:
    void print()
    {
        cout<<"Date<int,double>"<<endl;
    }
private:
    int a1=0;
    double a2=0;
};

3.2.2 偏特化

部分参数仍然使用模板,部分参数特化

template<class T>
class Date<T,double>
{
public:
    void print()
    {
        cout<<"Date<int,double>"<<endl;
    }
private:
    T a1=0;
    double a2=0;
};

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

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

相关文章

在Microsoft SQL Server 2008中,语法生成错误“并行数据仓库(PDW)功能未启用“

案例&#xff1a; 原表有两列&#xff0c;分别为月份、月份销售额&#xff0c;而需要一条 SQL 语句实现统计出每个月份以及当前月以前月份销售额和 sql 测试数据准备&#xff1a; DECLARE Temp Table ( monthNo INT, --- 月份 MoneyData Float --- 金额 ) insert INTO TEM…

利用AIGC,零成本靠谱副业,每天半小时,挣个生活费不成问题,亲测,省时省力攻略

文章目录 背景为什么是写作如何操作申请各大创作平台的账号吸引人关注&#xff0c;增加粉丝利用AIGC来创作 看看其他好友分享的收入情况 背景 最近&#xff0c;尝试利用AIGC在头条号、微信公众号上开始写文章&#xff0c;并且通过这个机会赚取了一笔的外快。这个经历让我深刻认…

最强整理,HttpRunner接口自动化框架-hook机制实战,一篇上高速...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 对于使用 Python结…

谷歌广告(Google ads)如何投放?新手必看的超全教程

Google是公认的全球最大的搜索引擎&#xff0c;同时&#xff0c;Google还通过旗下的 YouTube、Gmail、Google Play、Android等产品&#xff0c;汇集了海量的海外用户。对于跨境出海商家来说&#xff0c;谷歌广告是提高销售额、提高产品流量、拓展全球市场的重要推广渠道。 那么…

从0开始搭建一个Monorepo模版,基于Turborepo+pnpm+changesets+dumi

Monorepo 前言开始一、使用turborepo初始化项目二、调整目录结构及文件1. 调整package.json文件2. 调整app目录3. 调整eslint包4. 调整ui包5. 调整eslint配置6. 调整.npmrc7. 使用commitizen规范代码提交8. 使用commitlinthusky进行 commit提交信息校验9. 使用husky进行commit前…

vue3 - 使用reactive定义响应式数据进行列表赋值时,视图没有更新的解决方案

文章目录 1&#xff0c;问题2&#xff0c;原因3&#xff0c;解决方案一、再封装一层数据&#xff0c;即定义属性名&#xff0c;在后期赋值的时候&#xff0c;对此属性进行直接赋值三、使用数组的splice来直接更改原数组三、使用 ref 来定义数据 1&#xff0c;问题 在Vue 3.0 中…

pythonocc进阶学习:投影projection

1.点 到 线, 直线,曲线,等上的投影 staticmethod # 点到Lin的投影 def Project_Pnt_To_Lin(p: gp_Pnt, lin: gp_Lin):Edge BRepBuilderAPI_MakeEdge(lin).Edge()curve BRep_Tool.Curve(Edge)proPnt GeomAPI_ProjectPointOnCurve(p, curve[0])NearestPoint proPnt.Nearest…

JVM虚拟机篇

JVM组成 面试题1&#xff1a;什么是程序计数器&#xff1f; 面试题2&#xff1a;你能给我详细的介绍Java堆吗? 面试题3&#xff1a;什么是虚拟机栈&#xff1f; 面试题4&#xff1a;垃圾回收是否涉及栈内存&#xff1f; 垃圾回收主要指就是堆内存&#xff0c;当栈帧弹栈以后…

全开源国外购物商城手机APP页面装修功能开发

搭建一个全开源国外购物商城手机APP页面装修功能开发需要以下步骤&#xff1a; 1. 确定需求&#xff1a;首先&#xff0c;需要明确页面装修功能的具体需求。例如&#xff0c;是否需要支持自定义布局、颜色、字体等&#xff1b;是否需要支持多种商品展示方式&#xff08;列表、…

misc学习(1)Bugku-社工-进阶收集

新手一枚&#xff0c;参考文献如下&#xff1a; Bugku&#xff1a;社工-进阶收集_bugku 社工 进阶收集_FW_ENJOEY的博客-CSDN博客 照片如图所示&#xff1a; 线索线&#xff1a; 1.百度识图 发现是大雁塔 2.主角家距离大雁塔一共有七站地铁&#xff0c;其中要进行中转。 同时…

噼里啪啦 图像分类篇

1. LeNet初始化权重的问题 由于我使用的是torch 1.10.0的版本&#xff0c;其Conv2d的init是使用asqrt(5) 我将这里的torch默认初始化改为a1之后&#xff0c;acc的对比如下&#xff1a;可以看出&#xff1a;更改初始化之后&#xff0c;5个epoch&#xff0c;acc提高了3个点。改为…

数组相关练习

数组练习 将数组转化成字符串数组拷贝求数组元素的平均值查找数组中指定元素(顺序查找)二分查找冒泡排序数组逆序 将数组转化成字符串 import java.util.Arrays;public class Text1 {public static void main(String[] args) {int[] arr {5, 6, 4, 2};System.out.println(Arr…

机器学习中训练数据的重要性

人工智能技术发展至今&#xff0c;训练数据的重要性已经是我们老生常谈的问题。在重声训练数据为什么重要之前&#xff0c;我们先重新回顾下AI技术大爆炸的三大初始概念&#xff1a;机器学习是什么&#xff1f;人工智能是什么&#xff1f;训练数据又是什么&#xff1f; 机器学…

《Kali渗透基础》14. 无线渗透(四)

kali渗透 1&#xff1a;相关工具1.1&#xff1a;Aircrack-ng1.1.1&#xff1a;airmon-ng1.1.2&#xff1a;airodump-ng1.1.3&#xff1a;aireplay-ng1.1.4&#xff1a;airolib-ng1.1.5&#xff1a;bessid-ng 1.2&#xff1a;JTR1.3&#xff1a;Evil Twin Attacker1.4&#xff1…

Vue 中使用 WebWorker

目录 安装 loader 应用场景 打包时错误处理 安装 loader npm install worker-loader -D 如果直接把worker.js放到public目录下&#xff0c;则不需要安装loader vue.config.js const { defineConfig } require(vue/cli-service)module.exports defineConfig({transpileDe…

JS逆向系列之猿人学爬虫第14题-备而后动-勿使有变

文章目录 题目地址参数分析参考jspython 调用往期逆向文章推荐题目地址 https://match.yuanrenxue.cn/match/14题目难度标的是困难,主要难在js混淆部分。 参数分析 初始抓包有无限debugger反调试,可以直接hook 函数构造器过掉无限debugger Function.prototype.__construc…

C++异常体系

文章目录 一.C对运行时错误的处理方式函数调用链中的异常机制 二.异常的使用规范三.C异常体系C标准库中的异常体系 四.关于C异常的注意事项 一.C对运行时错误的处理方式 传统的C语言处理运行时错误采用的是assert或者错误码的方式,这种异常处理机制对错误信息的定位和描述能力…

ArcGIS Pro技术应用(暨基础入门、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合)

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

lc137. 只出现一次的数字 II

数组排序&#xff0c;既和前不一样又和后不一样的就是唯一的一个 public static int numberOnce(int[] nums) {Arrays.sort(nums);if (nums.length > 2 && nums[0] ! nums[1]) {//避免只有一个元素的数组return nums[0];}if (nums.length > 2 && nums[nu…

994. 腐烂的橘子

题目描述&#xff1a; 主要思路&#xff1a; 宽度优先搜索&#xff0c;利用队列实现。 需要注意的是放进队列的时候就需要标记&#xff0c;而不是取出的时候再标记。 class Solution { public:int orangesRotting(vector<vector<int>>& grid) {int ngrid.siz…