C++展开模板参数包、函数参数包-(lambda+折叠表达式)
开门见山
以下代码可展开模板参数包和展开函数参数包。
// lambda+折叠表达式(需C++17)
#include <iostream>
using namespace std;// 1.展开模板参数包
template<typename ...T>
void Func1()
{([]() {cout << typeid(T).name() << endl;}(), ...);// 折叠表达式
}
// 2.展开函数参数包
template<typename ...T>
void Func2(T... args)
{([&args]() {cout << args << endl;}(), ...);// 折叠表达式
}
void main(){cout << "开门见山" << endl;cout << "1.展开模板参数包" << endl;Func1<char, bool, int, char, const char*>();cout << endl;cout << "2.展开函数参数包" << endl;Func2('c', true, 2, 'h', "CSDN越来越好。。。");
}
文章目录
- 开门见山
- 前言
- 提前声明
- 遇到的问题
- 一步一步理解过程
- 1.1 模板推断类型
- 1.2 参数包转发、递归解函数包
- 1.3 不用递归解包-外部函数-折叠表达式
- 1.4 不用递归解包-lambda函数-折叠表达式
- 1.5 lambda+折叠表达式解开模板参数包
- 1.6 回归组件代码
前言
提前声明
-
关于
我菜鸟一枚,文中的术语、代码、文字若有错误,欢迎指正
-
阅读本文需要了解的知识
模板、模板参数包、函数参数包、参数包转发、扩展参数包、折叠表达式、lambda函数。
-
本文目的
是记录自己遇到的C++语法问题如何运用所学知识慢慢理解的过程,不属于学习语法文章。
相关不理解知识点建议百度。
遇到的问题
-
需求说明
-
在Unity的游戏引擎中,Ctrl+D复制一个物体,新物体应该具有旧物体的各个组件。
-
组件列表有:TransformComponent、SpriteRendererComponent、CircleRendererComponent。
-
如果要复制这三个组件给新物体,假设Unity内部实现代码如下
template<typename Component> static void CopyComponentIfExists(Entity dst, Entity src) {if (src.HasComponent<Component>()) {// 新物体添加旧物体组件dst.AddOrReplaceComponent<Component>(src.GetComponent<Component>());} } void Scene::DuplicateEntity(Entity entity) {// 1.创建旧实体同名的新实体std::string name = entity.GetName();Entity newEntity = CreateEntity(name);// 2.复制组件CopyComponentIfExists<TransformComponent>(newEntity, entity);CopyComponentIfExists<SpriteRendererComponent>(newEntity, entity);CopyComponentIfExists<CircleRendererComponent>(newEntity, entity); }
-
-
简化代码
若组件列表有20个,那么CopyComponentIfExists这行代码需要20个,显然代码会冗余,应使用模板来简化此代码,代码如下
template<typename... Component> struct ComponentGroup { }; using AllComponents = ComponentGroup<TransformComponent, SpriteRendererComponent,CircleRendererComponent>;template<typename... Component> static void CopyComponentIfExists(Entity dst, Entity src) {([&]() {if (src.HasComponent<Component>()) {// 新物体添加旧物体组件dst.AddOrReplaceComponent<Component>(src.GetComponent<Component>());}}(), ...); } template<typename... Component> static void CopyComponentIfExists(ComponentGroup<Component...>, Entity dst, Entity src) {CopyComponentIfExists<Component...>(dst, src); } void Scene::DuplicateEntity(Entity entity) {// 1.创建旧实体同名的新实体std::string name = entity.GetName();Entity newEntity = CreateEntity(name);// 2.复制组件CopyComponentIfExists(AllComponents{}, newEntity, entity);//CopyComponentIfExists<TransformComponent>(newEntity, entity);//CopyComponentIfExists<SpriteRendererComponent>(newEntity, entity);//CopyComponentIfExists<CircleRendererComponent>(newEntity, entity); }
-
模板代码完成新物体拷贝旧物体组件的任务。
-
假设有20个组件类型,只要在AllComponents写上这20个组件,再调用CopyComponentIfExists(AllComponents{}, newEntity, entity);就不用像之前写20行代码。
-
但是缺点也产生了,就是代码变得很难理解了。
-
-
简化核心代码( tips:这里被下面过程称为 组件代码)
我看到上一小点的代码,不知模板如何运作的,于是写下以下简洁版代码,只要理解了以下代码等同于理解上一小点的代码
#include <iostream> using namespace std;struct TransformComponent {TransformComponent() { cout << "TransformComponent()" << endl; } }; struct SpriteRendererComponent {SpriteRendererComponent() { cout << "SpriteRendererComponent()" << endl; } }; struct CircleRendererComponent {CircleRendererComponent() { cout << "CircleRendererComponent()" << endl; } };template<typename... Component> struct ComponentGroup {ComponentGroup() { cout << "ComponentGroup()" << endl; } }; using AllComponents = ComponentGroup<TransformComponent, SpriteRendererComponent,CircleRendererComponent>;// 为复制实体的辅助方法 template<typename... Component> static void CopyComponentIfExists() {([]() {cout << typeid(Component).name() << endl;}(), ...); } template<typename... Component> static void CopyComponentIfExists(ComponentGroup<Component...>) {CopyComponentIfExists<Component...>(); } void main() {CopyComponentIfExists(AllComponents{}); }
这简化核心版的代码(别称:组件代码)最重要的就是理解:如何展开模板参数包
由于在网上搜索相关模板参数知识点,都是讲如何展开函数参数包的,没有讲如何展开模板参数包(也许我自己菜,没有搜到),所以无奈,在自己花了一下午的时间,自己慢慢试出来了,编码理解过程如下。
一步一步理解过程
1.1 模板推断类型
-
参考例子1
#include <iostream> #include <string> using namespace std;template<typename T> static void Func1(T t) {T t2 = 8;cout << t << " " << t2 << endl; } template<typename T> static void Func2() {T t = 7;cout << t << endl; } void main() {Func1<int>(3);Func1(4);// 这就是省略了声明模板类型,由参数4推断Func1的模板T类型为intFunc2<int>(); }
-
所以可以理解以下代码
template<typename... Component> struct ComponentGroup {ComponentGroup() { cout << "ComponentGroup()" << endl; } }; using AllComponents = ComponentGroup<TransformComponent,SpriteRendererComponent,CircleRendererComponent>;template<typename... Component> static void CopyComponentIfExists(ComponentGroup<Component...>) {// 传给CopyComponentIfExists的模板参数包,为当前函数参数包推断出来的模板参数包CopyComponentIfExists<Component...>(); } void main() {// 传入AllComponents{}代表传入实参,CopyComponentIfExists的模板类型从实参推断出来CopyComponentIfExists(AllComponents{}); }
在CopyComponentIfExists函数内
-
ComponentGroup<>,是带有模板参数包的struct
-
ComponentGroup<Component…>,
Component…模板参数包由函数参数包AllComponents{}推断出来,像上面例子1的Func1(4);
由于AllComponents = ComponentGroup<TransformComponent,SpriteRendererComponent,CircleRendererComponent>
传入的函数参数包AllComponents{}
则能推断出模板参数包为
Component… = TransformComponent, SpriteRendererComponent,CircleRendererComponent -
得到了Component…模板参数包后,再当做显示的模板传给下个函数
代码CopyComponentIfExists<Component…>();
相当于
CopyComponentIfExists<TransformComponent, SpriteRendererComponent,CircleRendererComponent>();
-
1.2 参数包转发、递归解函数包
-
此小节为理解
- 参数包转发
- 解函数包概念
-
则给出以下代码
例子2:显示指定模板参数包类型、并传入函数参数包(与上有点不同,为理解解包概念)、并递归解函数包
#include <iostream> using namespace std;// 2.递归解函数包 void Func2() { cout << "Func2(),因为解包完了,无参数,就调用此函数,代表递归解包结束" << endl; }// 递归终止函数template<typename T, typename ...TT> void Func2(T& val, TT... args) // 函数参数包的第一个赋给第一个参数,剩下的都给参数包args {cout << val << "-->" << typeid(val).name() << endl;// 打印获取当前参数包的第一个参数值和类型// 继续解包,将函数参数包传给本函数递归,不指定模板参数包类型,由函数参数包推断出来Func2(args...); } // 1.参数包转发 template<typename... T> void Func1(T... args) {/*传给Func2函数的模板参数包,为当前函数参数包推断出来的模板参数包,并将多个实参传入与CopyComponentIfExists<Component...>();不同,这里有传递实参*///Func2<T...>(args...); // 转发模板参数包,函数参数包Func2<int, int, char, char const*>(args...);// 与上一段代码调用一样,只不过显示指定模板参数包类型 } void main() {cout << "参数包转发、递归解包"<< endl;Func1(2, 3, 'c', "12312"); }
1.3 不用递归解包-外部函数-折叠表达式
-
引入
由1.2的例子解包,知道了解包概念和流程,但是为靠近一开始的组件代码,不使用递归而实现的解包代码如下
#include <iostream> using namespace std;// 2.不用递归解包 template <class T> void Func2(T val) {cout << val << "-->" << typeid(val).name() << endl; } template<typename ...T> void Func2(T... args) {/* 重点在这:(func(args), ...);意思是逐个展开args函数参数包,并将解开的一个参数传入Func2,有多少个参数就有多少个Func2的调用。可以理解展开的语句为:Func2(2), Func2(3), Func2('c'), Func2("12312");*/(Func2(args), ...);// 折叠表达式解包 } // 1.参数包转发 template<typename... T> void Func1(T... args) {/*传给Func2函数的模板参数包,为当前函数参数包推断出来的模板参数包,并将多个实参传入与CopyComponentIfExists<Component...>();不同,这里有传递函数参数包*/Func2<T...>(args...); //Func2<int, int, char, char const*>(args...);// 与上一段代码调用一样 } void main() {cout << "不用递归解包-外部函数"<< endl;// 相当于Func2(2, 3, 'c', "12312");但为了与前一致讲述参数包转发,所以还是Func1Func1(2, 3, 'c', "12312"); }
1.4 不用递归解包-lambda函数-折叠表达式
由1.3例子的重点那段注释,Func2(args)可以改写成lambda匿名函数
#include <iostream>
using namespace std;// 2.不用递归解包-并用lamda
template<typename ...T>
void Func2(T... args)
{/*重点在这:([&](){}(), ...);意思是逐个展开args函数参数包,并将解开的一个参数被lambda捕获,有多少个参数就有多少个lambda的调用。可以理解展开的语句为:args = 2; [&]() {cout << args << "-->" << typeid(args).name() << endl; }(); args = 3; [&]() {cout << args << "-->" << typeid(args).name() << endl; }(); args = 'c'; [&]() {cout << args << "-->" << typeid(args).name() << endl; }(); args = "12312"; [&]() {cout << args << "-->" << typeid(args).name() << endl; }();实际上args函数参数包被展开的新变量名为:args_0、args_1、args_2、args_3*/// & 隐式引用捕获的是解开args函数参数包,得到一个参数赋给args的变量([&]() {cout << args << "-->" << typeid(args).name() << endl;}(), ...);// 折叠表达式解包
}
// 1.参数包转发
template<typename... T>
void Func1(T... args) {Func2<T...>(args...);
}
void main() {cout << "不用递归解包-lambda" << endl;Func1(2, 3, 'c', "12312");
}
1.5 lambda+折叠表达式解开模板参数包
由1.4的例子发现与原组件代码很接近了,但是原组件代码并没有函数参数包传递,只有推断出来的模板参数包传递。
于是问题再于是否能像1.4代码用lamda解函数参数包那样解开模板参数包呢?答案是肯定的,如下
#include <iostream>
using namespace std;// 2.用lamda解开模板参数包
template<typename ...T>
void Func2()
{// & 隐式引用捕获的是解开模型参数包,得到一个类型赋给T的变量// 但是不写&也行,1.4节要写也许args是参数在函数体内,而这里T是类型且在函数体外所以不用?([]() {cout << typeid(T).name() << endl;}(), ...);// 折叠表达式
}
// 1.模板参数包转发
template<typename... T>
void Func1(T... args) {// 模板参数包的由参数包推断出来// <T...> = <int, int, char, const char*>,转发模板参数包Func2<T...>();
}
void main() {cout << "类似组件(代码)-模板参数包转发-lambda解包" << endl;Func1(2, 3, 'c', "12312");
}
1.6 回归组件代码
由以上的步骤,不难理解原本的组件代码意思和流程了
- 传入AllComponents{}代表传入实参
- 第一个函数的模板参数包由函数参数包推断出来
- 第一个函数推断出来的模板参数包转发,传递给第二个函数
- 第二个函数接收到传过来的模板参数包
- 用折叠表达式解开模板参数包,并用lambda输出
#include <iostream>
using namespace std;struct TransformComponent {TransformComponent() { cout << "TransformComponent()" << endl; }
};
struct SpriteRendererComponent {SpriteRendererComponent() { cout << "SpriteRendererComponent()" << endl; }
};
struct CircleRendererComponent {CircleRendererComponent() { cout << "CircleRendererComponent()" << endl; }
};template<typename... Component>
struct ComponentGroup {ComponentGroup() { cout << "ComponentGroup()" << endl; }
};
using AllComponents = ComponentGroup<TransformComponent, SpriteRendererComponent,CircleRendererComponent>;// 4.接收到传过来的模板参数包
// <Component...> = <TransformComponent, SpriteRendererComponent,CircleRendererComponent>
template<typename... Component>
static void CopyComponentIfExists() {// 5.解开模板参数包,并用lambda输出([]() {cout << typeid(Component).name() << endl;}(), ...);// (, ...)折叠表达式解包
}
// 2.模板参数包由函数参数包推断出来
template<typename... Component>
static void CopyComponentIfExists(ComponentGroup<Component...>) {// 3.推断出来的模板参数包转发,传递CopyComponentIfExists<Component...>();// <Component...> = <TransformComponent, SpriteRendererComponent,CircleRendererComponent>
}
void main() {cout << "1.6 回归组件代码"<<endl;CopyComponentIfExists(AllComponents{});// 1.传入AllComponents{}代表传入实参
/*
由于:AllComponents{} = ComponentGroup<TransformComponent, SpriteRendererComponent,CircleRendererComponent>()
所以:CopyComponentIfExists(AllComponents{});等价CopyComponentIfExists(ComponentGroup<TransformComponent, SpriteRendererComponent, CircleRendererComponent>());
*/
}
相关文章:
C++展开模板参数包、函数参数包-(lambda+折叠表达式)
开门见山 以下代码可展开模板参数包和展开函数参数包。 // lambda折叠表达式(需C17) #include <iostream> using namespace std;// 1.展开模板参数包 template<typename ...T> void Func1() {([]() {cout << typeid(T).name() << endl;}(), ...);// …...
【Spark分布式内存计算框架——Spark Core】7. RDD Checkpoint、外部数据源
第五章 RDD Checkpoint RDD 数据可以持久化,但是持久化/缓存可以把数据放在内存中,虽然是快速的,但是也是最不可靠的;也可以把数据放在磁盘上,也不是完全可靠的!例如磁盘会损坏等。 Checkpoint的产生就是…...
Connext DDSQoS参考
1 QoS策略列表 ConnextDDS 6.1.1版中所有QoS策略的高级视图。 1. QoS策略描述...
【正则表达式】获取html代码文本内所有<script>标签内容
文章目录一. 背景二. 思路与过程1. 正则表达式中需要限定<script>开头与结尾2. 增加标签格式的限定3. 不限制<script>首尾的内部内容4. 中间的内容不能出现闭合的情况三. 结果与代码四. 正则辅助工具一. 背景 之前要对学生提交的html代码进行检查,在获…...
有 9 种springMVC常用注解高频使用,来了解下?
文章目录1、Controller2、RequestMapping2.1 RequestMapping注解有六个属性2.1.1 value2.1.2 method2.1.3 consumes2.1.4 produces2.1.5 params2.1.6 headers2.2 Request Mapping("/helloword/?/aa")的Ant路径,匹配符2.3 Request …...
【ES6】掌握Promise和利用Promise封装ajax
💻 【ES6】掌握Promise和利用Promise封装ajax 🏠专栏:JavaScript 👀个人主页:繁星学编程🍁 🧑个人简介:一个不断提高自我的平凡人🚀 🔊分享方向:目…...
REDIS-持久化方案
我们知道redis是内存数据库,它的数据是存储在内存中的,我们知道内存的一个特点是断电数据就丢失,所以redis提供了持久化功能,可以将内存中的数据状态存储到磁盘里面,避免数据丢失。 Redis持久化有三种方案,…...
五、Java框架之Maven进阶
黑马课程 文章目录1. 分模块开发1.1 分模块开发入门案例示例:抽取domain层示例:抽取dao层1.2 依赖管理2. 聚合和继承2.1 聚合概述聚合实现步骤2.2 继承 dependencyManagement3. 属性管理3.1 依赖版本属性管理3.2 配置文件属性管理(了解&#…...
1.前言【Java面试第三季】
1.前言【Java面试第三季】前言推荐1.前言00_前言闲聊和课程说明本课程介绍目前考核的变化趋势vcr集数和坚持学长谷粉面试题复盘反馈最后前言 2023-2-1 12:30:05 以下内容源自 【尚硅谷Java大厂面试题第3季,跳槽必刷题目必扫技术盲点(周阳主讲࿰…...
06分支限界法
文章目录八数码难题普通BFS算法全局择优算法(A算法,启发式搜索算法)单源最短路径问题装载问题算法思想:队列式分支限界法优先队列式分支限界法布线问题最大团问题批处理作业调度问题分支限界法与回溯法的区别: &#x…...
Docker Compose编排
一、概念1、Docker Compose是什么Docker Compose的前身是Fig,它是一个定义及运行多个Docker容器的工具通过 Compose,不需要使用shell脚本来启动容器,而使用 YAML 文件来配置应用程序需要的所有服务然后使用一个命令,根据 YAML 的文…...
Docker进阶 - 11. Docker Compose 编排服务
注:本文只对一些重要步骤和yml文件进行一些讲解,其他的具体程序没有记录。 目录 1. 原始的微服务工程编排(不使用Compose) 2. 使用Compose编排微服务 2.1 编写 docker-compose.yml 文件 2.2 修改并构建微服务工程镜像 2.3 启动 docker-compose 服务…...
福利篇2——嵌入式岗位笔试面试资料汇总(含大厂笔试面试真题)
前言 汇总嵌入式软件岗位笔试面试资料,供参考。 文章目录 前言一、公司嵌入式面经1、小米1)面试时长2)面试问题2、科大讯飞1)面试时长2)面试题目3、其余公司面经二、嵌入式笔试面试资料(全)三、嵌入式岗位薪资报告四、硬件岗位薪资报告一、公司嵌入式面经 1、小米 1)…...
[ubuntu]LVM磁盘管理
LVM是 Logical Volume Manager(逻辑卷管理)的简写,是Linux环境下对磁盘分区进行管理的一种机制,由Heinz Mauelshagen在Linux 2.4内核上实现。LVM可以实现用户在无需停机的情况下动态调整各个分区大小。1.简介 LVM本质上是一个…...
开源流程引擎Camunda
开源流程引擎Camunda 文章作者:智星 1.简介 Camunda是一个轻量级的商业流程开源平台,是一种基于Java的框架,持久层采用Mybatis,可以内嵌集成到Java应用、SpringBooot应用中,也可以独立运行,其支持BPMN&a…...
【PTA Advanced】1155 Heap Paths(C++)
目录 题目 Input Specification: Output Specification: Sample Input 1: Sample Output 1: Sample Input 2: Sample Output 2: Sample Input 3: Sample Output 3: 思路 代码 题目 In computer science, a heap is a specialized tree-based data structure that s…...
Educational Codeforces Round 129 (Rated for Div. 2)
A. Game with Cards. 题目链接 题目大意: Alice和Bob玩卡牌。Alice有n张,Bob有m张。第一轮选手出一张数字卡牌。第二轮另一个选手要选择一张比他大的,依此类推。谁没有牌可出则输。问Alice和Bob分别先手时,谁赢?输出…...
[数据库]表的增删改查
●🧑个人主页:你帅你先说. ●📃欢迎点赞👍关注💡收藏💖 ●📖既选择了远方,便只顾风雨兼程。 ●🤟欢迎大家有问题随时私信我! ●🧐版权:本文由[你帅…...
分享77个JS菜单导航,总有一款适合您
分享77个JS菜单导航,总有一款适合您 77个JS菜单导航下载链接:https://pan.baidu.com/s/1e_384_1KC2oSTDy7AaD3og?pwdzkw6 提取码:zkw6 Python采集代码下载链接:https://wwgn.lanzoul.com/iKGwb0kye3wj class ChinaZJsSeleni…...
kubernetes -- 核心组件介绍以及组件的运行流程
常用组件大白话说 如果想要官方的,详细的信息,请看官方文档。 https://kubernetes.io/zh-cn/docs/concepts/overview/components/ 现在介绍一些核心的概念: etcd:存储所有节点的信息,节点上部署的容器信息等都存在数…...
微信小程序Springboot短视频分享系统
3.1小程序端 用户注册页面,输入用户的个人信息点击注册即可。 注册完成后会返回到登录页面,用户输入自己注册的账号密码即可登录成功 登录成功后我们可以看到有相关的视频还有视频信息,我的信息等。 视频信息推荐是按照点击次数进行推荐的&am…...
排序算法学习
文章目录前言一、直接插入排序算法二、折半插入排序算法三、2路插入排序算法四、快速排序算法学习前言 算法是道路生涯的一个巨大阻碍。今日前来解决这其中之一:有关的排序算法,进行实现以及性能分析。 一、直接插入排序算法 插入排序算法实现主要思想…...
常见漏洞之 struts2+ jboss
数据来源 本文仅用于信息安全的学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。 01 Struts2相关介绍 》Struts2概述 》Struts2历史漏洞(1) 》…...
leetcode470 用Rand7()实现Rand10()
力扣470 第一步:根据Rand7()函数制作一个可以随机等概率生成0和1的函数rand_0and1 调用Rand7()函数,随机等概率生成1,2,3,4,5,6,7 这时我们设置:生成1,2&a…...
JSON数据解析商品详情API
大家有探讨稳定获取商品主图、jiage、标题,及sku的完整解决方案。这个引起了我技术挑战的兴趣,然后各种网上资料查询,最终还是不负努力,找到更好的解决方案,不再出现任何滑块验证码,完全绕过,实…...
服务端开发Java面试复盘篇1
上周投了一些简历,约了8-9家面试,其中完成了3家的第一轮面试,由于面试的是Java 的实习生,感觉问的题目都比较基础,不过有些问题回答的不是很好,在这里对回答的不太好的题目做一下总结和复盘。 目录 一、后…...
Android框架WiFi架构
同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、wpa_supplicant:wpa_supplicant本身开源项目源码,被谷歌收购之后加入Android移…...
rt-thread 移植调试记录
rt-thread 移植调试记录 记录rt-thread移植的过程。这里移植仅仅是利用rt-thread源码目录已经移植好的文件,组建自己的工程,不需要自己编写汇编完成底层移植。 1. 搭建基础工程 这里使用的是正点原子的潘多拉开发板,MCU为stm32l475。需要先…...
红外线额温枪与红外线温度传感器的原理分析
额温枪主要针对测量人体额温基准而设计,使用也非常简单方便。测体温可以达到一秒即可准确测量。并且不需要接触人体,隔着空气即可一键测温。非常适合家庭、学校、企业等场所。 但是由于其精度原因(一般为 0.2 ℃,也有更低的&#…...
2023牛客寒假算法集训营4
目录A. [清楚姐姐学信息论](https://ac.nowcoder.com/acm/contest/46812/A)(数学)B. [清楚姐姐学构造](https://ac.nowcoder.com/acm/contest/46812/B)(数学 构造)C. [清楚姐姐学01背包(Easy Version)](https://ac.nowcoder.com/…...
宜春代做网站/调研报告万能模板
“传递开源力量,传承布道精神。”2023年度FISCO BCOS MVP(最有价值专家)认定开启,寻找热爱技术、热爱开源、乐于布道、关注个人成长的你! FISCO BCOS MVP是谁? FISCO BCOS开源社区贡献高质量技术内容的意见…...
淘宝客网站建设/如何做网站推广私人
归并排序 定义 基本思路:一个待排序记录构成的文件,可以看作是由多个有序子文件组成的,对有序子文件通过若干次使用归并的方法,得到一个有序文件。 归并是指将两个(或多个)有序子表合并成一个有序表的过…...
企业网站seo贵不贵/it培训
【产品介绍】: 含肽序列和末端的DBCO基团,由于增加了灵活性,Gly序列已 应用于融合蛋白应用中,没有侧链,序列作为可选的折叠断裂间隔物使用 例如,GGS是的折叠断裂连接器之一,可以很好地暴露融合…...
平面设计兼职/seo课培训
简介SpringBoot和Vue,前后端分离,我们开源一套漂亮的代码和一套整洁的代码规范,让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,热爱生…...
亚马逊品牌备案的网站怎么做/自媒体
1.在 https://get.adobe.com/cn/flashplayer/ 上选择需要下载版本---> ( YUM,适用于Linux (YUM) ); 2.进入root权限后,进入你的下载目录下(你所下载的flash-player所在的目录,比如我的就是在home/lafee/…...
用wp系统做网站/seo公司 杭州
1、js数据类型分析 (1)基础类型:string、number、boolean、null、undefined (2)引用类型:object-->json、array... 2、点运算 xxx.sss(对象.属性或方法) 任何数据类型都拥有属性…...