11,模板泛化、模板特化、所占字节数、继承实现模板展开、using循环命名展开可变参数

模板泛化、模板特化、所占字节数、继承实现模板展开、using循环命名展开可变参数

  • 模板泛化
  • 模板特化
    • 模板全特化
    • 通过模板偏特化获取类型所占字节数
      • 通过模板偏特化和宏获取类型所占字节数
        • ...ParamTypes和ParamTypes...的区别
    • 通过继承实现模板展开
        • using
    • 通过using循环命名的方式来展开可变参数

模板泛化

当前不知道是什么类型,调用时才知道是什么类型

advanced.h

#pragma once
#include <iostream>
using namespace std;//泛化:所有的模板参数类型都未定义,当使用时才知道
template<class T1,class T2/* typename ...ParamTypes*/>//类型和可变参数的类型都不知道
class FHello_Class
{
private:T1 a;T2 b;
};

学习.cpp

#include <iostream>
#include"advanced.h"int main()
{FHello_Class<int, float>A;return 0;
}

模板特化

特化是一种模板技术

模板全特化

必须写一个模板泛化做匹配,否则用不了

模板特化是一种将模板参数替换为具体类型或值的过程

#pragma once
#include <iostream>
using namespace std;//泛化:所有的模板参数类型都未定义,当使用时才知道
template<class T>
class FHello_Class
{
private:T1 a;T2 b;
};//特化(全特化):模板特化是一种将模板参数替换为具体类型或值的过程
template<>
class FHello_Class<int>//给定的类型一定是具体的
{};
/*template<>
class FHello_Class1<int, float>//因为没有对应的模板泛化,所以会报错
{};*///函数全特化
template<class T>
void FunTest()
{};
template<>
void FunTest<int>()
{}

模板特化分为模板全特化和模板偏特化

全特化(Full Specialization)和偏特化(Partial Specialization)是C++中模板特化的两种形式。

全特化是指对完整的模板进行特化,也就是将模板中的所有参数都替换为具体的类型或值。全特化通过使用特定的类型或值提供完整的特化定义。例如:

template<typename T, int N>
class Array
{
public:T elements[N];
};template<>
class Array<int, 5>
{
public:int elements[5];
};

在上面的例子中,我们定义了一个通用的模板类 Array,包含一个类型参数 T 和一个整数参数 N。然后我们使用全特化对类型参数为 int,整数参数为 5 的情况进行了特化。这意味着当我们使用 Array<int, 5> 这个类型时,将使用全特化的定义,即 Array<int, 5> 类型将有一个 int 类型的数组成员。

偏特化是指对模板中的部分参数进行特化。偏特化允许我们针对特定的参数组合提供不同的定义。例如:

template<typename T, typename U>
class Pair
{
public:T first;U second;
};template<typename T>
class Pair<T, T>
{
public:T element;
};

在上面的例子中,我们定义了一个通用的模板类 Pair,包含两个类型参数 TU。然后我们使用偏特化对两个类型参数相同的情况进行了特化。这意味着当我们创建 Pair<int, int> 这样的实例时,将使用偏特化的定义,即 Pair<int, int> 类型将只有一个 int 类型的成员 element

总之,全特化是对完整的模板进行特化,将所有参数都替换为具体的类型或值;而偏特化是对模板中的部分参数进行特化,允许我们为特定的参数组合提供不同的定义。这两种特化形式使得C++中的模板更加灵活和强大。

通过模板偏特化获取类型所占字节数

偏特化加可变参数

#pragma once
#include <iostream>
using namespace std;//泛化
template<class T,class ... ParamTypes>
class Flen
{
public:enum{Number = Flen<T>::Number + Flen<ParamTypes...>::Number//得到最终类型大小};
};//偏特化
template<class Last>
class Flen<Last>
{
public:enum{Number = sizeof(Last)};
};
#include <iostream>
#include"advanced.h"int main()
{Flen<int, float, double, int>len;//输出结果位20 (4+4+8+4+4)cout << len.Number << endl;return 0;
}

通过模板偏特化和宏获取类型所占字节数

…ParamTypes和ParamTypes…的区别

ParamTypes…:ParamTypes…将被展开为一系列类型参数,并传递给Flen模板的实例化
… ParamTypes:…是展开模板参数包的语法,而不是参数包本身的一部分。class … ParamTypes是用于展开模板参数包ParamTypes的语法

SpawnIndex<3, int, float, double>

在上面的代码中,3 是一个非类型模板参数,int, float, double 是类型模板参数。在 SpawnIndex 的定义中,
…ParamTypes 将类型模板参数完整地展开,将 int, float, double 分别作为一个参数展开。
而 ParamTypes … 将 int, float, double 作为一个整体展开。

advanced.h

#pragma once
#include <iostream>
using namespace std;//泛化
template<class T,class ... ParamTypes>
class Flen
{
public:enum{Number = Flen<T>::Number + Flen<ParamTypes...>::Number//得到最终类型大小};
};//偏特化
template<class Last>
class Flen<Last>
{
public:enum{Number = sizeof(Last)};
};#define A(Name, ...)Flen<__VA_ARGS__>Name;//通过__VA_ARGS__接收

学习.cpp

#include <iostream>
#include"advanced.h"int main()
{Flen<int, float, double, int>len;//输出结果位20 (4+4+8+4+4)cout << len.Number << endl;A(len2, int, float, double, int, long long);//输出结果位28 (4+4+8+4+4+8)cout << len2.Number << endl;return 0;
}

通过继承实现模板展开

using

using单独使用相当于为类或结构体起别名

using Helloc = SpawnIndex<10>::Type;

给类Type起了别名Helloc

advanced.h

#pragma once
#include <iostream>
using namespace std;//定义
template<int ...>
struct HelloIndex 
{};//展开的中间值
template<int N,int ...ParamTypes>//N代表最大的,...ParamTypes代表数字
struct SpawnIndex :SpawnIndex<N - 1, N - 1, ParamTypes ...>
{};//终止,如果没有此会无限循环
template<int ... ParamTypes>
struct SpawnIndex<0, ParamTypes ...>
{typedef HelloIndex<ParamTypes...>Type;
};

学习.cpp

#include <iostream>
#include"advanced.h"int main()
{using Helloc = SpawnIndex<10>::Type;//展开一个10,同时为类Type起了一个别名Helloc//using单独使用相当于给类或结构体起别名//查看展开cout << typeid(Helloc).name() << endl;//输出结果struct HelloIndex<0,1,2,3,4,5,6,7,8,9>SpawnIndex<3>::Type;//SpawnIndex<3>::Type;的展开过程// //匹配到struct SpawnIndex :SpawnIndex<N - 1, N - 1, ParamTypes ...>进行运算//struct SpawnIndex :SpawnIndex< 2, 2>//            //struct SpawnIndex< 2, 2> :SpawnIndex< 1, 1, 2>//                   N  ParamTypes      N  N  ParamTypes //左边是template<int N,int ...ParamTypes>的形式,右边是SpawnIndex<N - 1, N - 1, ParamTypes ...>// //struct SpawnIndex< 1, 1, 2> :SpawnIndex< 0, 0, 1, 2>//因为N值为0了,展开到这一步会终止,之后匹配struct SpawnIndex<0, ParamTypes ...>此模板,进行下面步骤// //typedef HelloIndex< 0, 1, 2>Type;return 0;
}

(ue4源码的模板综合了很多,如可变参数、特化、非特化)

通过using循环命名的方式来展开可变参数

advanced.h

#pragma once
#include <iostream>
using namespace std;//定义
template<int ...>
struct HelloIndex 
{};//通过using展开的中间值
template<int N, int ...ParamTypes>
struct SpawnIndex 
{using Type = typename SpawnIndex<N - 1,N - 1, ParamTypes...>::Type;//编译器先发现Type,之后发现后面代码,之后一层层递归,递归过程中参数会改变
};//循环终止
template<int ... ParamTypes>
struct SpawnIndex<0, ParamTypes ...>//最后在参数为0时运行最后模板
{typedef HelloIndex<ParamTypes...>Type;
};

学习.cpp

#include <iostream>
#include"advanced.h"int main()
{using Helloc = SpawnIndex<10>::Type;//查看展开cout << typeid(Helloc).name() << endl;//输出结果:struct HelloIndex<0,1,2,3,4,5,6,7,8,9>return 0;
}

typename 是一个关键字,用于在模板编程中指示一个依赖类型。它通常用于声明模板中的类型参数,用于告知编译器某个名字代表一个类型

问题:为什么又两个N-1
在模板参数SpawnIndex<N, int ...ParamTypes>中,N - 1在每次递归调用时作为新的模板参数传递给SpawnIndex,而N - 1, N - 1则是作为递归调用的模板参数。这两个参数的作用如下:

首先,在每次递归调用时,N - 1作为新的模板参数传递给SpawnIndex<N - 1, N - 1, ParamTypes...>::Type,它用于更新N的值,实现递减。这样,在每次递归时,N的值都会减小,直到最后递归到SpawnIndex<0, ParamTypes...>,从而触发递归终止的模板。

其次,在递归调用中,N - 1, N - 1作为ParamTypes的一部分被传递给递归调用的模板参数列表。这样,新的模板参数列表中会包含N - 1, N - 1, ParamTypes...,并被传递给下一次递归调用。这样,每次递归调用时,ParamTypes列表都会逐渐增长,记录了调用的历史信息。

综上所述,N - 1主要用于控制递归调用的次数,而N - 1, N - 1在递归调用中记录了每次递归的历史信息。

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

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

相关文章

开发一个文生图的功能

文章目录 效果开发环境原理核心代码代码仓库问题效果 开发环境 Python 3.10PyCharm原理 借助开源项目stable-diffusion,通过该项目封装python库diffusers,可以轻易的实现文生图的功能。 关于更多diffusers的功能请访问:https://huggingface.co/docs/diffusers/index 核心代…

css样式表属性

文章目录 css样式表属性colorbackground-colorfont-sizefont-weightfont-familyfont-styletext-decorationtext-indentline-height(line-height的概念)width、heightletter-spacingtext-aligndirectionwriting-modefont-variantborder-radiusopacitycursorvertical-alignmin-wi…

【数据结构与算法】十大经典排序算法-归并排序

&#x1f31f;个人博客&#xff1a;www.hellocode.top &#x1f3f0;Java知识导航&#xff1a;Java-Navigate &#x1f525;CSDN&#xff1a;HelloCode. &#x1f31e;知乎&#xff1a;HelloCode &#x1f334;掘金&#xff1a;HelloCode ⚡如有问题&#xff0c;欢迎指正&#…

如何用输入函数为数组赋值

在编写程序时我们经常使用数组&#xff0c;而数组的大小可能是很大的但是我们并不需要为每个元素都自己赋值&#xff0c;我们可能会自定义输入数组元素个数&#xff0c;我们应该如何实现通过输入函数为数组赋值呢&#xff1f; 目录 第一种&#xff1a; 第二种&#xff1a; 第一…

大数据bug-sqoop(二:sqoop同步mysql数据到hive进行字段限制。)

一&#xff1a;sqoop脚本解析。 #&#xff01;/bin/sh mysqlHost$1 mysqlUserName$2 mysqlUserPass$3 mysqlDbName$4 sql$5 split$6 target$7 hiveDbName$8 hiveTbName$9 partFieldName${10} inputDate${11}echo ${mysqlHost} echo ${mysqlUserName} echo ${mysqlUserPass} ec…

OpenCV之remap的使用

OpenCV中使用remap实现图像的重映射。 重映射是指将图像中的某一像素值赋值到指定位置的操作&#xff1a;g(x,y) f ( h(x,y) )&#xff0c; 在这里&#xff0c; g( ) 是目标图像, f() 是源图像, 而h(x,y) 是作用于 (x,y) 的映射方法函数。为了完成映射过程, 需要获得一些插值为…

TypeError: a bytes-like object is required, not ‘str‘

raceback (most recent call last): File "D:\pycharmcode\client.py", line 12, in <module> tcp_socket.send(send_data) TypeError: a bytes-like object is required, not str 使用socket进行ubuntu与windows通信时&#xff0c;发送数据时报了以上错…

LeetCode 面试题 01.04. 回文排列

文章目录 一、题目二、C# 题解 一、题目 给定一个字符串&#xff0c;编写一个函数判定其是否为某个回文串的排列之一。 回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。 回文串不一定是字典当中的单词。 点击此处跳转题目。 示例1&#xff1a; 输入&…

CSS3:图片边框

简介 图片也可以作为边框&#xff0c;以下是实例演示 注意 实现该效果必须添加border样式&#xff0c;且必须位于border-image-socure之前否则不会生效 实例 <html lang"en"><head><style>p {width: 600px;margin: 200px auto;border: 30px soli…

maven工具-maven的使用-镜像仓库、本地仓、IDEA使用maven

Maven 一、为什么使用maven 添加第三方jar包jar包之间的依赖关系处理jar包之间的冲突获取第三方jar包将项目拆分成多个工程模块实现项目的分布式部署 二、maven简介 ​ Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的…

2023.8 - java - 对象和类

public class Dog {String breed;int size;String colour;int age;void eat() {}void run() {}void sleep(){}void name(){} } 一个类可以包含以下类型变量&#xff1a; 局部变量&#xff1a;在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方…

基于STM32标准库智能风扇设计

目录 一&#xff0c;前言 二&#xff0c;系统方案选择 三&#xff0c;实体展示 工程分类 四&#xff0c;相关代码 PWM.c PWM.h AD.c AD.h 电机驱动程序 舵机驱动 一&#xff0c;前言 当今生活中&#xff0c;风扇已成为人们解暑的重要工具&#xff0c;然而使用风扇缓解…

CentOS系统环境搭建(九)——centos系统下使用docker部署项目

centos系统环境搭建专栏&#x1f517;点击跳转 关于Docker-compose安装请看CentOS系统环境搭建&#xff08;三&#xff09;——Centos7安装Docker&Docker Compose&#xff0c;该文章同样收录于centos系统环境搭建专栏。 Centos7部署项目 采用前后端分离的形式部署。使用Do…

【Sklearn】基于随机梯度下降算法的数据分类预测(Excel可直接替换数据)

【Sklearn】基于随机梯度下降算法的数据分类预测(Excel可直接替换数据) 1.模型原理2.模型参数3.文件结构4.Excel数据5.下载地址6.完整代码7.运行结果1.模型原理 随机梯度下降(Stochastic Gradient Descent,SGD)是一种优化算法,用于训练模型的参数以最小化损失函数。在分…

QT学习笔记-QT5.15编译及安装谷歌拼音输入法(QtInputMethod_GooglePinyin)

QT学习笔记-QT5.15编译及安装谷歌拼音输入法&#xff08;QtInputMethod_GooglePinyin&#xff09; 0、背景1、环境2、下载QtInputMethod_GooglePinyin源码3、使用MinGW64构建套件编译3.1 编译QtInputMethod_GooglePinyin源码3.2、部署tgtsmlInputContextPlugin输入法插件3.3、运…

Lombok注解在JSON化中,JSON生成额外生成字段问题

问题描述&#xff1a; 定义如下对象 Dataclass A{private String A;public String getC() {return "abab";}} 执行如下逻辑 Autowiredprivate ObjectMapper objectMapper;Testpublic void test4() throws Exception {A a new A();a.setA("a");System.ou…

分布式 - 服务器Nginx:一小时入门系列之负载均衡

文章目录 1. 负载均衡2. 负载均衡策略1. 轮询策略2. 最小连接策略3. IP 哈希策略4. 哈希策略5. 加权轮询策略 1. 负载均衡 跨多个应用程序实例的负载平衡是一种常用技术&#xff0c;用于优化资源利用率、最大化吞吐量、减少延迟和确保容错配置。‎使用 nginx 作为非常有效的HT…

【MySQL】如何使用Shared-memory协议(Windows)连接MySQL数据库

文章目录 【MySQL】如何使用Shared-memory协议(Windows)连接MySQL数据库连接MySQL的协议使用Shared-memory协议(Windows)连接MySQL步骤1&#xff1a;确认MySQL服务器已启用Shared-memory连接启动Shared-memory连接方法 步骤2&#xff1a;客户端使用shared-memory连接MySQL服务器…

神经网络基础-神经网络补充概念-55-为什么是ML策略

“ML策略”&#xff08;Machine Learning Strategies&#xff09;是指在解决机器学习问题时&#xff0c;采取的一系列方法、技巧和策略。选择适当的ML策略对于获得高质量的模型和结果非常重要。以下是为什么要考虑ML策略的一些原因&#xff1a; 问题适应性&#xff1a;不同的机…

2023 最新版网络安全保姆级指南,从 0 基础进阶网络攻防工程师

一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…