c++之说_12|模板

news2024/11/15 23:29:25

关于模板,至少我们要先了解几个概念

一:函数模板

二:类模板

三:模板特化

四:形参参数包

模板覆盖的东西太多  我目前也不了解太多

函数模板 语法

template<typename 类型名,typename 类型名,typename ...多参包类型名> 
//内部的typename可写多个 有时我们可能会看到 这里会写 class 意思大概是差不多的
 返回值 函数名(){};
如

template<typename arg, typename ...args>
void set(arg s, args... d)
{
	val = s;
	base::set(d...);
};

这里我们可以看到  template<typename arg, typename ...args>

模板形参类型 有 arg  和  args 形参包 

arg  自然就代表可接受一个参数类型,

而args 形参包也叫变参参数包 可接受多个参数

当然也有要求

一:

模板形参必须可推导,显示实例化也属于可推导

二:

有显示实例化当然也有隐式实例化

但隐式实例化有个要求

函数参数中必须可推导模板形参

template<typename arg, typename ...args>
void set(arg s, args... d)
{
	val = s;
};

模板形参在函数参数中直接用到了

可如此调用  这叫隐式实例化
set(10,'g',50,100); //arg = int   args = {char,int,int}

这叫显示实例化
set<int,char,int,int>(10,'g',50,100); //arg = int   args = {char,int,int}

你会说 我们要是在函数参数中没一一使用到 模板形参怎么办?

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

自然就要显示实例化了  注意必须可推导

set<obj_2>(10, 100);
// Obj = obj_2   agr = int  args ={int}

看就显示实例化了没用到的 Obj 模板形参  我们人看上去也是可推导的 

编译器是人写的所以逻辑也是有些符合我们的思维的

函数特化有两种

偏特化(部分特化)

全特化

说实在的我也不太懂  我只能把我懂的部分 说一下

首先我们先看看 函数模板的偏特化 与 全特化

//主模版
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

//特化版本   args* 形参包中 全为 指针类型
template<typename Obj, typename arg, typename ...args>
void set(arg* s, args*... d)
{
	Obj c(d..., s);
}

//特化版本  Ret(*s)(a...)  非成员函数的函数指针
template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

//全特化   
template<>
void set<obj_2,float,float>(float v, float d)
{
	obj_2 c(d, v);
}

//全特化
template<>
void set<obj_2>(float v, float d,char k)
{
	obj_2 c(d, v);
}

这里有个叫主模版的函数模板   用特化就必须得需要主模版  什么叫主模板?

我的理解中是  函数模板 最基本的哪个

 比如上文

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
    Obj c(s,d...);
};

这个模板可以容纳下面几个特化出来的函数参数的样子 

比如指针类型  非成员函数的函数指针类型  成员函数的函数指针类型 

也就是说它更加全面

你说都更加全面了 我为什么还有特化

这是为了  处理不同的情况嘛   就和函数重载时  处理不同情况一样

比如


int geti()
{
	return 50;
}


int b = 10, b2 = 100;
set<obj_2>(&b,&b2);
/*
调用的特化版本是
template<typename Obj, typename arg, typename ...args>
void set(arg* s, args*... d)
{
	Obj c(d..., s);
}
*/


set<obj_2>(&geti, &b2);
/*
调用的特化版本是
template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

*/

set<obj_1>(0.5f, 5.3f);
/*
调用主模板版本
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

*/

set<obj_2>(0.5f, 5.3f,'p');
/*
调用的特化版本是
template<>
void set<obj_2>(float v, float d,char k)
{
	obj_2 c(d, v);
}


*/

set<obj_2>(0.f);
/*
调用主模版
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

*/

有人可能看到 有些特化怎么 模板形参比主模版还多

template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

这就是模板特化的一部分特性  注意 特化版本的模板形参与主模版的模板形参并无瓜葛 

就算他们是一样的名字 

但是实则是有一定要求的

比如

//主模板
template<typename Obj, typename arg>
void set1(arg c) { Obj c{}; };

//全特化
template<>
void set1<obj_2,float>(float c) { obj_2 bc{}; };

//错误特化版本  
template<>
void set1<obj_2,int,char>(int c) {};
/*
    这里我们注意到了, set1<obj_2,int,char>  有三个模板实参 
    而 我们的主模板只需要 两个模板实参  
    这就是要求:  
    不能大于主模版要求的模板形参数目

*/

//错误特化版本   与上述一样   
template<>
void set1<obj_2>(int c,char b) {};

和函数重载  很类似的规则

有人会问了 那你第一种怎么可写好多个模板实参?

注意主模版哦

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

一眼上去三个模板形参   但是我们最后是个模板形参包啊

不限个数的啊  超过两个的形参  统统进入形参包

这里可能有少年提出这样的写法

template<typename arg, typename ...args>
void set<obj_2>(arg s, args... d)
{
	obj_2 c(s,d...);
};

看着 嗯.....  我就特化处理  这个obj_2类型的 

不过可惜 这样写法是错误的

注意右边的 编译输出错误

好了函数模板的特化说完了

---------------------------------------------------

现在我们来看看类模板

      

上模板

//主模版
template<typename tp>
struct t1
{
};

//特化版本
template<typename Ret,typename Clss,typename ...Args>
struct t1 <Ret(Clss::*)(Args...)>
{
	using F = Ret(Clss::*)(Args...);
	t1(F fptr):ptr(fptr) {};

	F ptr;
};



int main()
{
	t1<decltype(&obj_2::gets)> b(&obj_2::gets);

	return 0;
}

和函数模板特化差不多

类模板可通过构造函数的参数推断模板形参

template<typename tp>
struct t1
{
	using type = tp;
	t1(tp p) :tpo(p) {};

	tp tpo;
};

template<typename Ret,typename Clss,typename ...Args>
struct t1 <Ret(Clss::*)(Args...)>
{
	using F = Ret(Clss::*)(Args...);
	t1(F fptr):ptr(fptr) {};

	F ptr;
};

int main()
{
	t1<decltype(&obj_2::gets)> b(&obj_2::gets);

	t1 b = t1(&obj_2::gets);//特化版本 struct t1 <Ret(Clss::*)(Args...)>
    // F = int(obj_2::*)(int,int,char);

	t1(obj_2()); // 推断的是主模版  tp = obj_2
	return 0;
}

类模板可以弥补我们之前函数模板的遗憾

template< typename tp>
struct t2<obj_2,tp>
{
	t2(tp p):op(p) {}
	tp op;
	obj_2 d;

};

它可以这样特化

但是又有可惜的事情了


t1 b3 = t1(obj_2()); //ok
auto c =    t2<obj_2,decltype(b3)>(b3); //ok  template< typename tp> struct t2<obj_2,tp>

auto c2 = t2<obj_2>(b3); // error  可惜不可以这样调用  至少我是能看出来 应该可以推导
template<typename obj,typename tp>
struct t2
{
	using type = tp;
	t2(obj* oj,tp p) :ptr(oj), tpo(p) {};
	obj* ptr;
	tp tpo;
};


//main 中

t1 b = t1(&obj_2::gets);
t1 b3 = t1(obj_2());
auto c3 = t2(&b, &b3);//可以

对了  忘记说一件事了

形参包 我们通过特化给它拆开

//主模版
template<typename ...T>
struct Tuple_text {};

//特化
template<>
struct Tuple_text<>
{
	void set() {};
};

//特化
template<typename Ty1, typename ...Ty2>
struct Tuple_text<Ty1, Ty2...> :public Tuple_text<Ty2...> {
	Ty1 val;
	using base = Tuple_text<Ty2...>;
	

	Tuple_text() {}
	
    template<typename arg, typename ...args>
	Tuple_text(arg a, args... d) :val(a), base(d ...) {}
	
	template<typename ...arg>
	void set(arg... args) {};
	template<>
	void set<>() {};

	template<typename arg, typename ...args>
	void set(arg s, args... d)
	{
		val = s;
		base::set(d...);
	};

	base& get()
	{
		return *this;
	}
};

这是今天学习到的  c++元组的类似做法

这里最关键的地方就是

template<>
struct Tuple_text<>
{
	void set() {};
};

template<typename Ty1, typename ...Ty2>
struct Tuple_text<Ty1, Ty2...> :public Tuple_text<Ty2...>
{

Ty1 val;
using base = Tuple_text<Ty2...>;


Tuple_text() {}
template<typename arg, typename ...args>
Tuple_text(arg a, args... d) :val(a), base(d ...) {}

}

没想到吧  我们的构造函数都能模板

这个有点复杂

我们展开看看   C++ Insights (cppinsights.io)  这个网站可以展开模板

#include <cstdio>

//主模板
template<typename ... T>
struct Tuple_text
{
};

//特化
template<>
struct Tuple_text<>
{
  inline void set()
  {
  }
  
};


//以下都为特化  实例化后其实也就是特化
template<>
struct Tuple_text<long> : public Tuple_text<>
{
  long val;
  using base = Tuple_text<>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<long>(long a)
  : Tuple_text<>()
  , val{a}
  {
  }
  
};


template<>
struct Tuple_text<float, long> : public Tuple_text<long>
{
  float val;
  using base = Tuple_text<long>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<float, long>(float a, long __d1)
  : Tuple_text<long>(__d1)
  , val{a}
  {
  }
  
};


template<>
struct Tuple_text<char, float, long> : public Tuple_text<float, long>
{
  char val;
  using base = Tuple_text<float, long>;
  inline Tuple_text();

  template<>
  inline Tuple_text<char, float, long>(char a, float __d1, long __d2)
  : Tuple_text<float, long>(__d1, __d2)
  , val{a}
  {
  }
  
  
};


template<>
struct Tuple_text<int, char, float, long> : public Tuple_text<char, float, long>
{
  int val;
  using base = Tuple_text<char, float, long>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<int, char, float, long>(int a, char __d1, float __d2, long __d3)
  : Tuple_text<char, float, long>(__d1, __d2, __d3)
  , val{a}
  {
  }
  
};



int main()
{
  Tuple_text<int, char, float, long> c = Tuple_text<int, char, float, long>(100, 'o', 6.0F, 500L);
  return 0;
}

注意main 函数里

我们看到这是一系列的继承关系

我们去vs 看看内存布局

我们看到  Tuple_text<int, char, float, long> 类里面有所有的 val 

那我们应该怎么拿到呢?

Tuple_text<int, char, float, long> 的 val 很简单

但是 继承的 父类 Tuple_text<char, float, long> 的 val

怎么拿呢?

我们要是能转换为 父类对象就好了

using base = Tuple_text<Ty2...>;

base& get()
{
	return *this;
}

这样是不是就能拿到父类对象了?

Tuple_text<int, char, float, long>  : Tuple_text<char, float, long> :

Tuple_text<float, long> : Tuple_text<long> : Tuple_text<>

每一级的 base 都是本级继承的父类

c.val   c.get().val  c.get().get().val  c.get().get().get().val

Tuple_text<> 这个是我们自己特化的类

是一个空的 所以继承链到此终结

模板编程是面向编译器的

很强大 但是也很难以解读

模板的玩法不只这些  玩法很多很多 看大家积累了 我也需要积累

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

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

相关文章

KMP算法|next指针|详细讲解学习

KMP 算法介绍 KMP 算法是基于串的朴素模式匹配算法优化的。 串的朴素模式匹配算法是将主串中所有的与模式串长度相等的子串与模式串进行比较&#xff0c;如果模式串与进行比较的的子串相等&#xff0c;就匹配成功&#xff0c;否则匹配失败。 在 KMP 算法的理解的基础上&#x…

SpringFramework实战指南(六)

SpringFramework实战指南(六) 4.4 基于 配置类 方式管理 Bean4.4.1 完全注解开发理解4.4.2 实验一:配置类和扫描注解4.4.3 实验二:@Bean定义组件4.4.4 实验三:高级特性:@Bean注解细节4.4.5 实验四:高级特性:@Import扩展4.4.6 实验五:基于注解+配置类方式整合三层架构组…

C#(C Sharp)学习笔记_前言及Visual Studio Code配置C#运行环境【一】

前言 这可以说是我第一次正式的踏入C#的学习道路&#xff0c;我真没想过我两年前是怎么跳过C#去学Unity3D游戏开发的&#xff08;当然了&#xff0c;游戏开发肯定是没有成功的&#xff0c;都是照搬代码&#xff09;。而现在&#xff0c;我真正地学习一下C#&#xff0c;就和去年…

调和平均

L1-4 调和平均 分数 10 作者 陈越 单位 浙江大学 N 个正数的算数平均是这些数的和除以 N&#xff0c;它们的调和平均是它们倒数的算数平均的倒数。本题就请你计算给定的一系列正数的调和平均值。 输入格式&#xff1a…

Springboot 整合 Elasticsearch(三):使用RestHighLevelClient操作ES ①

&#x1f4c1; 前情提要&#xff1a; Springboot 整合 Elasticsearch&#xff08;一&#xff09;&#xff1a;Linux下安装 Elasticsearch 8.x Springboot 整合 Elasticsearch&#xff08;二&#xff09;&#xff1a;使用HTTP请求来操作ES 目录 一、Springboot 整合 Elasticsea…

Pymysql之Connection中常用API

Connection中常用API 1、open() &#xff1a;检测数据库是否连接。 connect.open&#xff1a;如果数据库连接返回Trhe&#xff0c;否则返回False。 2、ping(reconnectTrue) connect.ping(reconnectTrue):如果reconnectTrue表示连接断开后&#xff0c;重新进行连接。 import…

Android Compose 一个音视频APP——Magic Music Player

Magic Music APP Magic Music APP Magic Music APP概述效果预览-视频资源功能预览Library歌曲播放效果预览歌曲播放依赖注入设置播放源播放进度上一首&下一首UI响应 歌词歌词解析解析成行逐行解析 视频播放AndroidView引入Exoplayer自定义Exoplayer样式横竖屏切换 歌曲多任…

Vue中使用 Element-ui form和 el-dialog 进行自定义表单校验清除表单状态

文章目录 问题分析 问题 在使用 Element-ui el-form 和 el-dialog 进行自定义表单校验时&#xff0c;出现点击编辑按钮之后再带年纪新增按钮&#xff0c;出现如下情况&#xff0c;新增弹出表单进行了一次表单验证&#xff0c;而这时不应该要表单验证的 分析 在寻找多种解决…

Google DeepMind最新研究,将视觉语言大模型作为强化学习的全新奖励来源

论文题目&#xff1a;Vision-Language Models as a Source of Rewards 论文链接&#xff1a;https://arxiv.org/abs/2312.09187 在大型语言模型&#xff08;LLM&#xff09;不断发展的进程中&#xff0c;强化学习扮演了重要的角色&#xff0c;ChatGPT就是在GPT-3.5的基础上经过…

Stable Diffusion 模型下载:RealCartoon-Pixar - V8

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 这个检查点是从 RealCartoon3D 检查点分支出来的。它的目标是在整体上产生更多的“皮克斯”风格。我非常喜欢3D卡通的外观,希望能够创建出具有

Linux死机排查方法——内存日志

一般情况下&#xff0c;Linux系统在死机时会产生一些dump信息&#xff0c;例如oops&#xff0c;通过分析oops信息就可以基本定位问题所在&#xff0c;但有些特殊情况下死机时&#xff0c;没有任何的打印的信息。如果直接使用printk等打印排查问题&#xff0c;有可能会因为print…

ssm+vue的校园一卡通密钥管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的校园一卡通密钥管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

240207-3步设置VSCode插件Inline-Bookmarks自定义颜色及名称

Step 1: 插件安装 Step 2: 配置文件 "inline-bookmarks.expert.custom.styles": {"default": {"gutterIconColor": "#157EFB","overviewRulerColor": "rgba(21, 126, 251, 0.7)","light": {"fontW…

使用HCPpipelines分割皮层

前段时间阅读了一篇文献,文章的做法我比较感兴趣,所以打算学习一下文献的做法。文章的最开始一部分是使用HCPpipelines对T1和T2像进行皮层分割,调用的是freesurfer6。https://github.com/Washington-University/HCPpipelines 一、工作环境准备 1.安装好FSL,版本在6.0.2以上…

H2和流行关系型数据库对比

1.H2和SQLite数据库对比 1.1.独特的特点和用途 H2 和 SQLite 是两个流行的轻量级数据库&#xff0c;它们各自有一些独特的特点和用途&#xff1a; H2 数据库: 主要用于 Java 应用&#xff0c;因为它是用 Java 编写的。支持内存模式和磁盘持久化。提供了一个基于浏览器的控制台…

Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi

文章目录 简介Asp .Net Core 集成 Panda.DynamicWebApi配置原理什么是POCO Controller&#xff1f;POCO控制器原理ControllerFeatureProvider实现自定义判断规则IApplicationModelConventionPanda.DynamicWebApi中的实现ConfigureApiExplorer()ConfigureSelector()ConfigurePar…

板块零 IDEA编译器基础:第三节 下载和在IDEA中集成 Tomcat服务器 来自【汤米尼克的JAVAEE全套教程专栏】

板块零 IDEA编译器基础&#xff1a;第三节 下载和在IDEA中集成 Tomcat服务器 一、为什么选择Tomcat&#xff08;1&#xff09;常见的JAVA WEB服务器&#xff08;2&#xff09;选择Tomcat的理由 二、Tomcat 8.5下载解压三、Tomcat 结构目录四、在IDEA中集成Tomcat 假设我们已经…

基于STM32平台的嵌入式AI音频开发

加我微信hezkz17&#xff0c;可申请加入 嵌入式人工智能开发交流答疑群。 1 stm32芯片AI开发流程 其中模型也可以选择tensorflow &#xff0c;pytorch 2 FP-AI-SENSING1 SDK开发包介绍 3 声音场景分类项目数据集选择 (1)自己采集数据打标签 (2) 使用专用数据集 4 完整参考

如何使用phpStudy搭建网站并结合内网穿透远程访问本地站点

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

Nacos(1)

Nacos注册中心 主要解决问题 假如微服务被调用较多&#xff0c;为了应对更高的并发&#xff0c;进行了多实例部署 此时&#xff0c;每个微服务的实例其IP或端口不同&#xff0c;问题来了&#xff1a; 这么多实例&#xff0c;如何知道每一个实例的地址&#xff1f;http请求要…