网页模板库/网站快速优化排名软件
文章目录
- 1.非类型模板
- 2.模板特化
- 2.1.类模板特化
- 2.1.1.全特化
- 2.1.2.偏特化
- 2.2.函数模板特化
- 3.函数模板声明定义分离
之前我们学习的模板能达到泛型的原因是:使用了“泛型的类型”,但是如果经过后面的“造轮子”(后面会尝试实现一下
STL
的一些类模板),就会很明显发现泛型不仅仅是类型的问题,例如:“适配器”的使用(在后面双端队列里有体现),实际上就是一种泛型,对于泛型的理解我们不能仅限于类型。
1.非类型模板
模板除了类型模板,还有非类型模板。
-
类型模板:出现在模板的参数列表中,跟在
class
或者typname
后的参数类型名称 -
非类型模板:使用一个常量作为类的一个非类型模板参数,在模板类/模板函数中可以将该参数作为常量来使用,且不能修改。并且,这里非类型模板参数也可以使用缺省值
//没有非类型模板参数
#include <iostream>
using namespace std;
#define NUM 10template<class T>
class Data
{
public://...
private:T _arr[NUM];
};
int main()
{Data<int> a1;//无法修改初始化大小(注意是初始化的时候修改大小)//只能手动调整#define的值//和之前的typedef的问题类似Data<double> a2;
}
这个时候就可以使用非类型模板参数,这个参数是一个常量,更加准确来说是不可被修改的整形常量(包括布尔类型)。
//有非类型模板参数
#include <iostream>
using namespace std;
//#define NUM 10template<class T, size_t N = 50>
class Data
{
public://...
private:T _arr[N];
};
int main()
{Data<int> a1;//默认初始化申请50个空间Data<double, 20> a2;//初始化时申请20个空间
}
您可能会疑惑:为什么不可以初始化先使用new
开辟固定的空间,等到后续操作进行扩容操作呢?注意这里只是利用这个例子来简述语法特性,并不是实际的用途(在后续“位图”等知识中有很大的价值)。
补充:除了使用这个常量,还可以将这个常量作为一个类的标识数字来使用。
函数模板也可以使用这一特性。
#include <iostream>
using namespace std;template<class T, size_t N = 50>
class Data
{
public://...
public:T _arr[N];
};
template<class T, long NUM = 50>//演示了其他整形
void function(T& i)
{i = NUM;
}int main()
{Data<int, 10> a1;Data<int, 100> a2;int i = 0;function<int, 200>(i);//演示了函数修改非类型模板参数cout << i << endl;
}
C++ 11
搞的新容器:静态数组array
,其类模板就是使用了这个非类型模板参数。
#include <iostream>
#include <array>
using namespace std;
int main()
{array<int, 10>arr;for (auto &i : arr){i = 10;}for (auto i : arr){cout << i << " ";}cout << endl;return 0;
}
可惜静态数组不会进行初始化(吐槽:std::array
当参数传递仍然要把数组的长度传过去,挺好玩的…),也支持范围for
,并且越界检查比较严格(传统数组是抽查,但是静态数组是读写越界全面检查,避免代码崩溃)。
嘛…感觉优势不算很大(大不了使用vector
,这也可以查找越界,还可以使用列表初始化)所以推广并不高。这个容器有点为了强迫症而统一STL
风格的感觉。
类似deque
在list
和vector
的感觉(后面会讲),静态数组就是传统数组和vector
之间的方案。
2.模板特化
通常模板可以实现和类型无关的代码,但是有一些特殊的类型可能会得到一些错误的、不符合预期的结果,因此需要进行特殊处理,这就有了“模板特化”这个概念。
2.1.类模板特化
2.1.1.全特化
#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 _d1;T2 _d2;
};template<>//全特化,必须要写这句
class Data<int, char>//这里指定了特定的类型
{
public:Data(){cout << "Data<int, char>" << endl;}
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;Data<int, char> d2;//这样就会直接调用全特化的模板,不会再去类模板构造
}
int main()
{TestVector();
}
2.1.2.偏特化
除了全特化,还可以进行偏特化。在下述代码中,我们可以看到偏特化不仅只是做了一些类型的指定,也可以对类型做进一步限制。
#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data(const T1& d1, const T2& d2) : _d1(d1), _d2(d2){ cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//1.部分类模板参数特化
template <class T1>
class Data<T1, int>
{
public:Data(const T1& d1, const int& d2) : _d1(d1), _d2(d2){ cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};//2.1.对两个参数进行进一步限制,偏特化为指针类型
template <typename T1, typename T2>//这里也是必须写,和全特化有些不同
class Data <T1*, T2*>
{
public:Data(const T1& d1, const T2& d2) : _d1(d1), _d2(d2){ cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;//注意其成员不是指针,仍然是原类型T2 _d2;//注意其成员不是指针,仍然是原类型
};//2.2.对两个参数进行进一步限制,偏特化为引用类型
template <typename T1, typename T2>//这里也是必须写,和全特化有些不同
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2) : _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl; }
private:T1 _d1;T2 _d2;
};void test()
{Data<int, double> d1(10, 20);//调用基础的类模板Data<int, int> d2(30, 40);//调用偏特化的类模板Data<int*, int*> d3(1, 2);//调用偏特化的指针版本Data<int&, int&> d4(3, 4);//调用偏特化的引用版本
}
int main()
{test();return 0;
}
补充:偏特化会使得特化更加强大,某些程度上来说比全特化更加常用。
因此可以总结类模板的特化语法就是:
//1.原类模板
template<class T1, class T2, /*...*/, class Tn>
class ClassName
{/*...*/};//2.特化类模板
template</*填入仍旧继续使用的泛型(如果都使用可以省略这里)*/>
class ClassName</*指定特定的类型,并且写入仍旧使用的泛型,注意顺序*/>
{/*...*/};
2.2.函数模板特化
#include <iostream>
using namespace std;
//类模板
class Data
{
public:Data(int d) : _d(d) {}bool operator<(const Data& x){ return _d < x._d; }
private:int _d;
};//函数模板
template<class T>
bool Less(T left, T right)
{return left < right;
}//特化函数模板
template<>
bool Less<Data*>(Data* left, Data* right)
{return (*left) < (*right);
}
/*
template<>
bool Less<Data*>(const Data* & left, const Data* & right)//这种写法很特殊,是没有办法通过的,原本是为了使用const修饰引用变量,避免引用变量被修改,但是由于指针和const修饰的特殊性,导致const修饰了*,因此只能改成:(Data* const& left, Data* const& right)这种写法虽然奇怪,但是却是正确的。
{return (*left) < (*right);
}
*/int main()
{cout << Less(1, 2) << endl;//调用了普通的函数模板Data d1(1);Data d2(2);cout << Less(d1, d2) << endl;//调用了普通的函数模板Data* p1 = &d1;Data* p2 = &d2;cout << Less(p1, p2) << endl;//调用特化后的函数模板,虽然这种调用看起来很奇怪return 0;
}
注意
1
:区分好“匹配”和“特化”和“实例化”。
- 匹配:是有相匹配的类型,可以使用对应的模板
- 实例化:是编译器自己做的,将匹配对应的模板进行实例化
- 特化:特化不是全新的模板,必须依赖模板,不可以单独存在
注意
2
:实际上特化更加适合类模板一些,实际上函数重载(重载)对比函数模板特化(匹配)更加简单。
3.函数模板声明定义分离
这一点凸显在函数的声明定义的分离上,假设有下面三个文件:
//function.h内声明
#pragma once
#include <iostream>
template<class T>
T Add(const T& left, const T& right);int NoTemplateAdd(const int& left, const int& right);
//function.cpp内定义
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int NoTemplateAdd(const int& left, const int& right)
{return left + right;
}
//main.cpp内包含头文件并且调用
#include "function.h"
int main()
{std::cout << Add(1, 2);//链接错误std::cout << Add(1.0, 2.0);//链接错误std::cout << NoTemplateAdd(1, 2);//成功调用return 0;
}
可以发现函数模板没有办法声明和定义分离在两个文件中,会显示链接错误(但是普通的函数可以)。
让我们来分析一下这里面的原因:
C/C++
要运行程序,就需要经历“预处理-编译-汇编-链接”- 在编译阶段,会对多份源文件做各自的编译(进行词法、语法、语义分析、错误检查等)并且生成多份的汇编代码(注意头文件是不会参与编译的)这个时候在
function.obj
或者说function.o
中,由于编译器没有看到函数的实例化,因此没有生成具体的加法函数。 - 而在
main.obj
或者main.o
中,编译器看到有加法函数的调用,但是暂时不知道具体的实现,因此就暂时放进了符号表里等待后续链接 - 在链接阶段由于没有实例化,因此
function.obj
或者说function.o
中没有加法函数的定义,根本就无法提供加法函数的地址在符号表里供main.obj
或者main.o
链接
因此后续链接的时候就会报错,即“链接错误”。
如果一定要分离,有两种方法:
-
进行显示实例化(有缺陷)
//function.h #include <iostream> using namespace std;template <typename T> void MyFunction(T value);
//function.cpp #include "function.h"template <typename T> void MyFunction(T value) {cout << value << endl; } //显式实例化int类型的函数模板 template void MyFunction<int>(int value);
//main.cpp #include "function.h"int main() {//调用int版本的函数模板MyFunction(42);return 0; }
-
在一个翻译单元里分离,即:干脆直接将定义和声明都写在一个
.hpp
内,这样做是更加推荐的。
相关文章:

C++类与对象(下)
文章目录 1.非类型模板2.模板特化2.1.类模板特化2.1.1.全特化2.1.2.偏特化 2.2.函数模板特化 3.函数模板声明定义分离 之前我们学习的模板能达到泛型的原因是:使用了“泛型的类型”,但是如果经过后面的“造轮子”(后面会尝试实现一下 STL的一…...

SpringBoot——》引入Redis
推荐链接: 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…...

C# newtonsoft序列化将long类型转化为字符串
/// <summary> /// 转化为json的时候long类型转为string /// </summary> public class LongJsonConverter: JsonConverter {public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer){try{return r…...

黑马点评-02使用Redis代替session,Redis + token机制实现
Redis代替session session共享问题 每个Tomcat中都有一份属于自己的session,所以多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务时可能会导致数据丢失 用户第一次访问1号tomcat并把自己的信息存放session域中, 如果第二次访问到了2号tomcat就无法获取到在1号…...

arm 点灯实验代码以及现象
.text .global _start _start: 1.设置GPIOE寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28 LDR R0,0x50000A28 LDR R1,[R0] ORR R1,R1,#(0x1<<4) 第4位置1 STR R1,[R0] 1.设置GPIOF寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28 LDR R…...

选择适合普通公司的项目管理软件
不管是打工人还是学生党都适合使用Zoho Projects项目管理软件。利用项目概览功能,将整体项目尽收眼底,作为项目管理者,项目日程、进度都可见,Zoho Projects项目管理APP助推项目每一环节的进展,更便于管理者设计项目的下…...

E (1081) : DS堆栈--逆序输出(STL栈使用)
Description C中已经自带堆栈对象stack,无需编写堆栈操作的具体实现代码。 本题目主要帮助大家熟悉stack对象的使用,然后实现字符串的逆序输出 输入一个字符串,按字符按输入顺序压入堆栈,然后根据堆栈后进先出的特点࿰…...

访问者模式 行为型设计模式之九
1.定义 在不改变数据结构的前提下,增加作用于一组对象元素的新功能。 2.动机 访问者模式适用于数据结构相对稳定的系统它把数据结构和作用于数据结构之上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式的目的是要把处理从数据结构…...

JVM垃圾回收之JVM GC算法探究
JVM垃圾回收之JVM GC算法探究 在Java虚拟机(JVM)中,垃圾回收(Garbage Collection,GC)是自动管理内存的重要机制,它负责回收程序中不再使用的对象所占用的内存。GC算法是垃圾回收的核心…...

Django 前端模板显示换行符、日期格式
linebreaksbr 显示换行符 <td>{{ data.sku_list|default:"无"|linebreaksbr }}</td> date:"Y年m月d日 H:i" 设置日期格式 <td>{{ data.submit_time|date:"Y年m月d日 H:i" }}</td> 其他语法 forloop 获取循环的索引 …...

Aurora中的策略模式和模板模式
Aurora中的策略模式和模板模式 在aurora中为了方便以后的扩展使用了策略模式和模板模式实现图片上传和搜索功能,能够在配置类中设置使用Oss或者minio上传图片,es或者mysql文章搜索。后续有新的上传方式或者搜索方式只需要编写对应的实现类即可ÿ…...

Ubuntu 22.04 安装系统 手动分区 针对只有一块硬盘 lvm 单独分出/home
自动安装的信息 参考自动安装时产生的分区信息 rootyeqiang-MS-7B23:~# fdisk /dev/sdb -l Disk /dev/sdb:894.25 GiB,960197124096 字节,1875385008 个扇区 Disk model: INTEL SSDSC2KB96 单元:扇区 / 1 * 512 512 字节 扇区大…...

Android系统定制之监听USB键盘来判断是否弹出软键盘
一.项目背景 在设备上弹出软键盘,会将一大部分UI遮挡起来,造成很多图标无法看到和点击,使用起来不方便,因此通过插入usb键盘输入代替软键盘,但是点击输入框默认会弹出软键盘,因此想要插入USB键盘时,默认关闭软键盘,拔出键盘时再弹出,方便用户使用 二.设计思路 2.1…...

LeakyReLU激活函数
nn.LeakyReLU 是PyTorch中的Leaky Rectified Linear Unit(ReLU)激活函数的实现。Leaky ReLU是一种修正线性单元,它在非负数部分保持线性,而在负数部分引入一个小的斜率(通常是一个小的正数),以防…...

Qt单一应用实例判断
原本项目中使用QSharedMemory的方法来判断当前是否已存在运行的实例,但在MacOS上,当程序异常崩溃后,QSharedMemory没有被正常销毁,导致应用程序无法再次被打开。 对此,Qt assistant中有相关说明: 摘抄 qt-s…...

企业AI工程化之路:如何实现高效、低成本、高质量的落地?
MLOps工程实践 概述面临挑战目的内容简介读者对象专家推荐目录 写在末尾: 主页传送门:📀 传送 概述 作为计算机科学的一个重要领域,机器学习也是目前人工智能领域非常活跃的分支之一。机器学习通过分析海量数据、总结规律&#x…...

最短路径专题8 交通枢纽 (Floyd求最短路 )
题目: 样例: 输入 4 5 2 0 1 1 0 2 5 0 3 3 1 2 2 2 3 4 0 2 输出 0 7 思路: 由题意,绘制了该城市的地图之后,由给出的 k 个编号作为起点,求该点到各个点之间的最短距离之和最小的点是哪个,并…...

文件扫描模块
文章目录 前言文件扫描模块设计初级扫描方案一实现单线程扫描整合扫描步骤 设计初级扫描方案二周期性扫描 总结 前言 我们这个模块考虑的是数据库里面的内容从哪里获取。 获取完成后,这时候,我们就需要把目录里面文件/子文件都获取出来,并存入数据库。 文件扫描模…...

MySQL之主从复制
概述: 将主库的数据 变更同步到从库,从而保证主库和从库数据一致。 它的作用是 数据备份,失败迁移,读写分离,降低单库读写压力 原理: 主服务器上面的任何修改都会保存在二进制日志( Bin-log日志…...

[leetcode 单调栈] 901. 股票价格跨度 M
设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度 。 当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。 例如,如果未来 7 天股票的价格是 [100…...

Java线程池:并发编程的利器
Java线程池:并发编程的利器 在多任务、高并发的时代,Java并发编程显得尤为重要。其中,Java线程池是一种高效的管理线程的工具,能够提高应用程序的性能和响应速度。本文将深入探讨Java线程池的工作原理、应用场景以及简单示例&…...

ARM硬件断点
hw_breakpoint 是由处理器提供专门断点寄存器来保存一个地址,是需要处理器支持的。处理器在执行过程中会不断去匹配,当匹配上后则会产生中断。 内核自带了硬件断点的样例linux-3.16\samples\hw_breakpoint\data_breakpoint.c static void sample_hbp_h…...

Java使用WebSocket(基础)
准备一个html页面 <!DOCTYPE HTML> <html> <head><meta charset"UTF-8"><title>WebSocket Demo</title> </head> <body><input id"text" type"text" /><button onclick"send()&…...

图像处理与计算机视觉--第五章-图像分割-自适应阈值分割
文章目录 1.自适应阈值分割介绍2.自适应阈值函数参数解析3.高斯概率函数介绍4.自适应阈值分割核心代码5.自适应阈值分割效果展示6.参考文章及致谢 1.自适应阈值分割介绍 在图片处理过程中,针对铺前进行二值化等操作的时候,我们希望能够将图片相应区域内所…...

记一次问题排查
1785年,卡文迪许在实验中发现,把不含水蒸气、二氧化碳的空气除去氧气和氮气后,仍有很少量的残余气体存在。这种现象在当时并没有引起化学家的重视。 一百多年后,英国物理学家瑞利测定氮气的密度时,发现从空气里分离出来…...

【Spring Boot】创建一个 Spring Boot 项目
创建一个 Spring Boot 项目 1. 安装插件2. 创建 Spring Boot 项目3. 项目目录介绍和运行注意事项 1. 安装插件 IDEA 中安装 Spring Boot Helper / Spring Assistant / Spring Initializr and Assistant插件才能创建 Spring Boot 项⽬ (有时候不用安装,直…...

flutter中使用缓存
前言 在flutter项目中使用ListView或者PageView等有滚动条组件的时候,切换页面的时候,再切换回来会丢失之前的滑动状态,这个时候就需要需要使用缓存功能 缓存类 import package:flutter/material.dart;class KeepAliveWrapper extends Sta…...

京东数据分析平台:9月中上旬白酒消费市场数据分析
9月份,围绕白酒的热点不断。9月5日,瑞幸咖啡官微发布消息称,瑞幸与贵州茅台跨界合作推出的酱香拿铁刷新单品纪录,首日销量突破542万杯,销售额破1亿元。9月14日,贵州茅台官微发布消息称与德芙推出联名产品“…...

Linux安装 spark 教程详解
目录 一 准备安装包 二 安装 scala 三 修改配置文件 1)修改 workers 文件 2)修改 spark-env.sh文件 四 进入 spark 交互式平台 一 准备安装包 可以自行去 spark 官网下载想要的版本 这里准备了 spark3.1.2的网盘资源 链接: https://pan.baidu.com…...

动态内存管理函数(malloc,calloc,realloc,free)
动态内存函数 1.1malloc和free C语言提供了一个动态内存开辟的函数: void* malloc (size_t size); 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。 如果开辟成功,则返回一个指向开辟好空间的指针。如果开辟失败&#…...