当前位置: 首页 > news >正文

【C++、C++11】可变参数模板、lambda表达式、包装器

文章目录

  • 📖 前言
  • 1. 可变参数模板
    • 1.1 万能模板:
    • 1.2 完美转发:
    • 1.3 可变参数模板的使用:
    • 1.4 emplace_back:
  • 2. lambda表达式
    • 2.1 lambda表达式的定义:
    • 2.2 lambda表达式的用法:
      • 2.2 - 1 捕捉列表的用法:
    • 2.3 lambda表达式的类型名称:
  • 3. 包装器
    • 3.1 bind绑定:

📖 前言

上一篇我们已经将C++11开了个头,详细讲述了C++11中的 { } 列表初始化和右值引用,本章将继续讲解C++11中的一些新的且比较实用的功能。

列表初始化 + 右值引用复习:👉 传送门


1. 可变参数模板

C++11的新特性可变参数模板:

  • 能够让创建可以接受可变参数的函数模板和类模板,相比C++98 / C++03
  • 类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进
  • 然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的

1.1 万能模板:

万能模板的有样式:
在这里插入图片描述

  • 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值
  • 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力
  • 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值

很显然这并不是我们想要的,因为无论传的是左值还是右值,都将退化成左值:

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }//std::forward<T>(t)在传参的过程中保持了t的原生类型属性。template<typename T>
void PerfectForward(T&& t)
{//完美转发,按照原封不动的方式进行转发//Fun(t);Fun(std::forward<T>(t));
}int main()
{PerfectForward(10);           //右值int a;PerfectForward(a);            //左值PerfectForward(std::move(a)); //右值const int b = 8;PerfectForward(b);		      //const 左值PerfectForward(std::move(b)); //const 右值return 0;
}

在这里插入图片描述

如图所示全部都是调用左值引用了,这可能是编译器底层实现的原因,但是这显然不是一个好的现象。


1.2 完美转发:

我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的

完美转发:
在这里插入图片描述
在这里插入图片描述
C++11新提供的forward函数模板,可以解决上述问题,使得传右值不会退化成左值,。


1.3 可变参数模板的使用:

首先在学C语言的时候,我们用的printf函数是一个可变参数函数,printf从语法上说是可以写多个参数,然后该函数自己识别。

可变参数模板:
在这里插入图片描述
解释:

  • 可变模板参数,源自于printf(可变参数函数),…代表可变参数
  • Args是一个模板参数包,args是一个函数形参参数包
  • 声明一个参数包Args…args,这个参数包中可以包含0到任意个模板参数

模板的可变类型参数,不仅不知道有多少个,还不知道类型,要推演出来。

template <class ...Args>
void ShowList1(Args... args)
{//参数个数cout << sizeof...(args) << endl;
}//不一定非要用Args也可以取别的名字
template <class ...X>
void ShowList2(X... y)
{cout << sizeof...(y) << endl;
}int main()
{ShowList1(1, 'x', 1.1);ShowList2(1, 2, 3, 4, 5);return 0;
}

运行结果是:3 5

参数包展开:

//方法一://递归到最后一个,就找最匹配的那一个
template <class T>
void ShowList(const T& val)
{cout << val << "->" << typeid(val).name() << "end" << endl;
}//编译时递归去推
//整个推导的过程是在编译的时候进行的(这是个编译的过程 -- 编译时决议)
template <class T, class ...Args>
void ShowList(const T& val, Args... args)
{cout << sizeof...(args) << endl;cout << val << "->" << typeid(val).name() << endl;//去递归解析ShowList(args...);
}//方法二://0个模板参数走这个 -- 作为递归的终结
void ShowList()
{}int main()
{ShowList(1, 'x', 1.1);cout << endl;//有几个参数都能推ShowList(1, 2, 3, 4, 5);return 0;
}

在这里插入图片描述

  • 类似于递归的一个过程,但是不是递归,最后都有一个结束条件,参数包中的模板参数是一个一个减少的。
  • 整个推导的过程是在编译的时候进行的(这是个编译的过程 – 编译时决议)

用参数包定义数组:

template <class ...Args>
void ShowList(Args... args)
{//*列表初始化 -- 这种方法不通用//C++只允许数组里面是同一个类型的int arr[] = { args... };cout << endl;
}int main()
{//ShowList(1, 'x', 1.1, vector<int>{2, 2});这种就不适用 -》 int arr[] = { args... };//cout << endl;ShowList(1, 2, 3, 4, 5);ShowList(10, 20, 30);return 0;
}

在这里插入图片描述

template <class T>
int PrintArg(const T& t)
{cout << t << " ";return 0;
}template <class ...Args>
void ShowList(Args... args)
{//列表初始化//int arr[] = { (PrintArg(args), 0)...};int arr[] = { PrintArg(args)... };cout << endl;
}int main()
{ShowList(1, 'x', 1.1, string("hello world"));cout << endl;ShowList(1, 2, 3, 4, 5);//ShowList(10, 20, 30);return 0;

在这里插入图片描述


1.4 emplace_back:

在这里插入图片描述
C++11STL中新增了一个尾插的方法:

  • emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
  • 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
  • 可以不传pair,可以直接传参数
  • 拿到参数自己构建对象(保证了底层一定是右值 —— 直接去构造的)

在这里插入图片描述

push_back():

  • 参数如果给的是左值是:构造 + 拷贝构造
  • 参数如果给的是右值是:构造 + 移动构造
  • 函数传值返回也是个右值

emplace_back():

  • 直接构造

这两者其实差距不大

2. lambda表达式

在仿函数那一节我们知道,如果我们用每次为了实现一个sort算法,因为每次比较的数据类型不同,都要重新去写一个类(仿函数),如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便,因此,在C++11语法中出现了Lambda表达式。

2.1 lambda表达式的定义:

定义了一个可以调用的对象 / 匿名函数,一般定义在局部,特点是可以深度绑定了局部的数据。

lambda表达式书写格式:

  • 普通函数有函数名,lambda函数没有函数名,他是一个整体
    在这里插入图片描述
  • lambda表达式,实际上是一个匿名函数,实际上是在定义一个函数(局部的函数)
  • lambda表达式通常是用来定义小函数
  • lambda表达式在局部是很好用的,最特别的就是捕捉列表

auto Add1 = [](int x, int y)->int{return x + y; }; Add叫lambda表达式的对象 – lambda定义的是一个对象。

lambda表达式各部分说明:

  • [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  • (parameters): 参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
  • mutable: 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  • ->returntype: 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
  • {statement}: 函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的量。

注意:

  • 在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。
  • 因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

2.2 lambda表达式的用法:

int main()
{int a = 0, b = 200;//一般是局部匿名函数  也可以写到全局(一般都不写返回值)//参数列表(无参的时候)和返回值(表达式自动推导)也可以省略掉auto Add1 = [](int x, int y)->double {return (x + y) / 3.0; };auto Add2 = [](int x, int y)->int {return (x + y) / 3.0; };//参数列表可以省略了auto Add3 = [a, b] {return (a + b) / 3.0; };//调用是和普通的函数一样的cout << Add1(a, b) << endl;cout << Add2(a, b) << endl;//调用没有区别,但是没有实参,因为捕捉了cout << Add3() << endl;
}
  • 一般是局部匿名函数 也可以写到全局(一般都不写返回值)
  • 参数列表(无参的时候)和返回值类型(表达式自动推导)也可以省略掉
  • 和普通函数调用没区别,但是没有实参,因为捕捉了

2.2 - 1 捕捉列表的用法:

用lambda表达式来实现交换两个数:

int main()
{int a = 0, b = 200;//方法一:auto Swap1 = [](int& x, int& y)->void {int tmp = x;x = y;y = tmp;};Swap1(a, b);cout << a << " " << b << endl;//方法二://mutable 只是让传值捕捉变量const属性去掉了//mutable要和()参数列表配在一起//可以认为里面的a,b还是外面a,b的拷贝 -- 里面改变外面还是不变//所以mutable实际没什么价值//这样写还是没有交换 -- 只是编译通过了但是达不到我们想要的效果//auto Swap2 = [a, b]()mutable->void {//	int tmp = a;//	a = b;//	b = tmp;//};//用引用的方式捕捉(按值捕捉不能改变)auto Swap2 = [&a, &b]()->void {int tmp = a;a = b;b = tmp;};Swap2();cout << a << " " << b << endl;
}

方法一:

  • 就像普通函数那样传引用传参

方法二:

  • 不能通过传值传参,首先捕捉列表捕捉的数据具有const属性,不能修改
  • 其次mutable可以让传值捕捉变量const属性去掉了,即使去掉了
    • mutable要和()参数列表配在一起
    • 所以mutable实际没什么价值
  • 可以认为里面的a,b还是外面a,b的拷贝 ,里面改变外面还是不变

所以只能按照引用的方式捕捉

各种混合捕捉:

int main()
{int c = 2, d = 3, e = 4, f = 5, g = 6, ret;//传值的方式捕捉全部对象auto Func1 = [=] {return c + d * e / f + g;};cout << Func1() << endl;//传引用捕捉全部对象auto Func2 = [&] {ret = c + d * e / f + g;};Func2();cout << ret << endl;//混着捕捉auto Func3 = [c, d, &ret] {ret = c + d;};Func3();cout << ret << endl;//ret传引用捕捉 其他全部传值捕捉auto Func4 = [=, &ret] {ret = c + d * e / f + g;//传值捕捉默认是加了const的//c = 1;};Func4();cout << ret << endl;return 0;
}

2.3 lambda表达式的类型名称:

每个lambda表达式类型的名字都是不一样的,先来看一段程序:

int main()
{auto Add = [](int x, int y)->int { return x + y; };cout << typeid(Add).name() << endl;return 0;
}

在这里插入图片描述
仿函数的名称后面叫uuid,一组随机字符串,通过某个算法得到的,使得每个lambda表达式的名字都不同。

lambda表达式的底层和仿函数的底层其实是一样的。

可以将lambda表达式赋值给相同类型的函数指针
在这里插入图片描述
不建议这样写。

3. 包装器

function包装器:
function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
在这里插入图片描述
模板参数说明:

  • Ret: 被调用函数的返回类型
  • Args…: 被调用函数的形参

看下面一段程序:

template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double f(double i)
{return i / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};int main()
{//函数名cout << useF(f, 11.11) << endl;//函数对象cout << useF(Functor(), 11.11) << endl;//lamber表达式cout << useF([](double d)->double{ return d / 4; }, 11.11) << endl;return 0;
}

在这里插入图片描述

  • func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是lamber表达式对象?
  • 所以这些都是可调用的类型!

如此丰富的类型,可能会导致模板的效率低下!

包装器可以很好的解决上面的问题:

在这里插入图片描述

  • function可以认为是一个类模板,包装可调用对象
  • 经过包装器包装之后得到的都是一个统一的类型function
  • 最后count的地址相同,并且count到3了,说明包装器是统一了类型

注意,特殊的包装:
在这里插入图片描述


3.1 bind绑定:

在这里插入图片描述
简介:

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。 一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

bind是个函数模板,调整了调用对象参数,用来调整个数和顺序,生成一个新的可调用对象:

包装器的特点就是统一了类型,但是值得注意的是包装时一定要注意匹配,如下:
在这里插入图片描述
这就在包装时不匹配,所在包装时一定要匹配。

下面的场景我们存一个map,这时就要求function包装器要统一了:
在这里插入图片描述
不过不免会有一些特殊情况:
在这里插入图片描述
func1和func2是可以的,但是func3就难受了呀,不匹配啊…

此时我们就可以通过bind函数来调整一下参数个数:

在这里插入图片描述
在这里插入图片描述
通过bind函数来调整顺序:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
调整顺序,用处不大。

相关文章:

【C++、C++11】可变参数模板、lambda表达式、包装器

文章目录&#x1f4d6; 前言1. 可变参数模板1.1 万能模板&#xff1a;1.2 完美转发&#xff1a;1.3 可变参数模板的使用&#xff1a;1.4 emplace_back&#xff1a;2. lambda表达式2.1 lambda表达式的定义&#xff1a;2.2 lambda表达式的用法&#xff1a;2.2 - 1 捕捉列表的用法…...

外贸主机测评

一、俄罗斯vps 服务商&#xff1a; JUSTG: Home - Sun Network Company Limited LOCVPS: LOCVPS 全球云 - 十年老牌 为跨境外贸/远程办公/网站建设提供澎湃动力 JUSTHOST: justhost.ru RUVDS: Gcorelabs: 二、主机测评指标&#xff1a; 1、速度、延迟、丢包、路由测试…...

Meta CTO:Quest 2生命周期或比预期更久

前不久&#xff0c;Meta未来4年路线图遭曝光&#xff0c;泄露了该公司正在筹备中的一些AR/VR原型。除此之外&#xff0c;还有消息称Quest Pro或因销量不佳&#xff0c;而不再迭代。毫无疑问&#xff0c;Meta的一举一动持续受到行业关注&#xff0c;而面对最近的爆料&#xff0c…...

Vector - CAPL - 文件处理函数

在当前平台化的趋势下,就算是协议层测试依然需要适配各种各样的项目,也需要处理各类型的文件,那我们如何对文件进行读取、写入、修改等类型的操作呢?今天我们就会介绍此类型的函数,主要适用于text、bin文件的处理。 打开文件 Open...

实力加持!RestCloud完成多方国产化适配,携手共建信创生态

近年来&#xff0c;随着数字化建设进入深水区&#xff0c;企事业单位对信息安全重视程度与日俱增&#xff0c;核心技术自主可控已成为时代呼唤&#xff0c;国产化浪潮日益汹涌澎湃。近日&#xff0c;RestCloud在国产化方面取得新进展&#xff0c;完成了全部产品线信创环境的多方…...

Unity 3D GUI教程||OnGUI TextArea 控件||OnGUI ScrollView 控件

OnGUI TextArea 控件 Unity 3D TextArea 控件用于创建一个多行的文本编辑区。用户可以在多行文本编辑区编辑文本内容。 该控件可以对超出控件宽度的文本内容实现换行操作。 TextArea 控件同样会将当前文本编辑区中的文本内容以字符串形式返回。 开发人员可以通过创建 Strin…...

Leetcode.828 统计子串中的唯一字符

题目链接 Leetcode.828 统计子串中的唯一字符 Rating &#xff1a; 2034 题目描述 我们定义了一个函数 countUniqueChars(s)来统计字符串 s中的唯一字符&#xff0c;并返回唯一字符的个数。 例如&#xff1a;s "LEETCODE"&#xff0c;则其中 "L", "…...

Hibernate 相关特性

1. Hibernate一般使用hql进行查询&#xff0c;但也有sql执行的方法 Native sql 查询,。需要注意的是&#xff0c;使用Native SQL查询可能会破坏Hibernate的缓存机制&#xff0c;并可能导致性能问题 String sql "SELECT * FROM users WHERE age > :age"; Query …...

【研究生学术英语读写教程翻译 中国科学院大学Unit1-Unit8】

Unit1 Descartes Was Wrong 笛卡尔错了:“他人在,故我在” Unit2 Are we ready for the next volcanic catastrophe?我们准备好应对下一次火山灾难了吗? Unit3 Theorists,experimentalists and the bias in popular physics理论家,实验家和大众物理学的偏见 unit4 Magic Nu…...

ListView 控件的使用

第一步&#xff1a;找到ListView的控件通过findViewById 找到ListView的控件 ListView listView findViewById(R.id.listView);第二步&#xff1a;创建Bean类 得到set和get的方法解析获取的数据创建Bean类 得到set和get的方法public class Bean {String nanm""; pub…...

域控制器搭建以及成员加入

需要iso&#xff1a;windows server 2016软件使用&#xff1a;vmwarewindows server 2016系统搭建自己选iso&#xff0c;一直下一步就可以安装完成。&#xff08;记得要设置密码&#xff09;&#xff08;密码要求大小写字母数字符号&#xff09;等待就能安装完成。安装和配置Ac…...

利用 MLP(多层感知器)和 RBF(径向基函数)神经网络解决的近似和分类示例问题(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 1、径向基神经网络 径向基函数网络是由三层构成的前向网络&#xff1a;第一层为输入层&#xff0c;节点个数的能与输入的维数&…...

进阶C语言——数据的存储【详解】

文章目录1. 数据类型介绍1.1 类型的基本归类2. 整形在内存中的存储2.1 原码、反码、补码2.2 大小端介绍2.3 练习3. 浮点型在内存中的存储3.1 一个例子3.2 浮点数存储的规则1. 数据类型介绍 前面我们已经学习了基本的内置类型&#xff1a; char //字符数据类型 short //短整型 …...

KUKA机器人修改机器人名称和IP地址的具体方法示例

KUKA机器人修改机器人名称和IP地址的具体方法示例 修改机器人名称 如下图所示,首先切换用户组到管理员,输入默认密码:kuka, 如下图所示,点击菜单键—投入运行—机器人数据, 如下图所示,此时可以看到机器人的名称为rrr445, 如下图所示,修改之后,点击左侧的“”…...

【数据分析师求职面试指南】必备基础知识整理

数据分析师基础知识统计 数据分析知识基础概念随机变量常用特征正态分布与大数定律、中心极限定律假设检验模型、数据挖掘知识常用概念数据集划分欠拟合过拟合模型分类方法常见模型介绍线性回归模型&#xff1a;逻辑回归模型决策树模型随机森林模型Boosting模型XGBoost模型模型…...

《开关电源宝典 降压电路(BUCK)的原理与应用》

嗨&#xff0c;硬件攻城狮或电源工程师同行们&#xff0c;我想写本专门解析BUCK电源电路的书籍&#xff0c;以下是“前言”内容的部分摘录以及当前的目录&#xff0c;当前已经完成22万多字500多页了&#xff0c;即使如此&#xff0c;离真正出版书籍&#xff0c;还有很长的路要走…...

R语言基础(一):注释、变量

R语言用于统计分析和绘制图表等操作。不同于Java等其它语言&#xff0c;R用于统计&#xff0c;而不是做一个网站或者软件&#xff0c;所以R的一些开发习惯和其它语言不同。如果你是一个编程小白&#xff0c;那么可以放心大胆的学。如果你是一个有编程基础的人&#xff0c;那么需…...

Java 集合进阶(二)

文章目录一、Set1. 概述2. 哈希值3. 元素唯一性4. 哈希表5. 遍历学生对象6. LinkedHashSet7. TreeSet7.1 自然排序7.2 比较器排序8. 不重复的随机数二、泛型1. 概述2. 泛型类3. 泛型方法4. 泛型接口5. 类型通配符6. 可变参数7. 可变参数的使用一、Set 1. 概述 Set 集合特点&am…...

小孩用什么样的台灯比较好?2023眼科医生青睐的儿童台灯推荐

小孩子属于眼睛比较脆弱的人群&#xff0c;所以选购护眼台灯时&#xff0c;选光线温和的比较好&#xff0c;而且调光、显色效果、色温、防蓝光等方面也要出色&#xff0c;否则容易导致孩子近视。 1、调光。台灯首先是照度高&#xff0c;国AA级&#xff0b;大功率发光&#xff0…...

Ubuntu c++ MySQL数据库操作

mysql安装sudo apt-get install updatesudo apt-get install mysql-server libmysqlclient-dev mysql-workbenchmysql启动/重启/停止sudo service mysql start/restart/stop登录mysql命令&#xff1a;mysql -uroot -p错误异常&#xff1a;解决办法&#xff1a;修改mysqld.cnf配…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...