【C++篇】引领C++模板初体验:泛型编程的力量与妙用
文章目录
- C++模板编程
- 前言
- 第一章: 初始模板与函数模版
- 1.1 什么是泛型编程?
- 1.1.1 为什么要有泛型编程?
- 1.1.1 泛型编程的优势
- 1.2 函数模板的基础
- 1.2.1 什么是函数模板?
- 1.2.2 函数模板的定义格式
- 1.2.3 示例:通用的交换函数
- 输出示例:
- 1.2.4 模板中的`typename`与`class`
- 1.3 函数模板的原理
- 1.3.1 函数模板的实例化
- 1.3.2 隐式实例化与显式实例化
- 第二章: 类模板
- 2.1 类模板概念
- 2.1.1 类模板的定义格式
- 2.1.2 示例:简单的类模板
- 输出示例:
- 2.2 类模板的实例化
- 2.3 类模板中的成员函数定义
- 2.4 为什么不建议类模板的定义和声明分离?
- 2.4.1 模板的编译时行为
- 2.4.2 链接器无法找到定义
- 2.4.3 无法预编译模板
- 2.4.4 解决方案:将声明和定义放在同一个头文件中
- 第三章: 模板的匹配原则
- 3.1 模板的匹配原则
- 写在最后
C++模板编程
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!
前言
C++作为一门强大的编程语言,以其丰富的功能和灵活的设计著称。模板编程是C++中非常重要的一个特性,通过模板可以实现泛型编程,编写与数据类型无关的代码,极大地提高了代码的复用性和可维护性。本文将从泛型编程、函数模板、类模板等几个方面详细讲解C++模板的使用,并结合实际的代码示例进行分析,帮助大家全面掌握模板编程的知识。
本篇文章将包含以下几个部分:
- 泛型编程的基本概念
- 函数模板的定义与使用
- 类模板的实现
- 模板的匹配原则
通过阅读本文,你将能够掌握C++模板编程的基础知识,理解其背后的工作原理,并学会如何在实际项目中应用这些技术。
第一章: 初始模板与函数模版
1.1 什么是泛型编程?
泛型编程(Generic Programming)是C++中的一种编程范式,旨在编写与数据类型无关的通用代码。这意味着你可以编写一次代码,并通过不同的数据类型进行复用。C++通过模板(Template)来实现泛型编程,模板是泛型编程的核心工具。
1.1.1 为什么要有泛型编程?
- 问题提出:如何实现一个通用的交换函数呢?
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
//....等等
使用函数重载虽然可以实现,但是有一下几个不好的地方:
- 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?

如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同
材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。这就是泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
1.1.1 泛型编程的优势
泛型编程的主要优势包括:
- 代码复用性强:通过模板,你可以避免为每个数据类型单独编写相同功能的代码。
- 提高代码的可维护性:代码只需编写一次,减少了冗余代码,后续如果需要修改或修复,只需在一处进行。
- 减少编写错误:重复编写代码时容易出错,而模板可以让编译器自动生成所需代码,减少人为失误。

1.2 函数模板的基础
1.2.1 什么是函数模板?
函数模板(Function Template)是一个与类型无关的函数“蓝图”。通过模板参数,编译器在编译期间会根据实际的数据类型生成相应的函数版本。
1.2.2 函数模板的定义格式
我们可以通过以下格式来定义一个函数模板:
template<typename T>
返回类型 函数名(参数列表) {// 函数体
}
template:告诉编译器接下来的内容是模板。typename T:定义一个模板参数T,可以用来表示任何类型。返回类型和参数列表可以使用T作为数据类型。
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替
class),下文会讲到
1.2.3 示例:通用的交换函数
那我们就可以使用模板来编写交换函数,它可以交换任意类型的数据:
/*** @brief 通用的交换函数* @tparam T 通用的类型参数,由编译器根据实参推断* @param left 左侧变量* @param right 右侧变量*/
template<typename T>
void Swap(T& left, T& right) {T temp = left;left = right;right = temp;
}int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;char c1 = 'A', c2 = 'B';// 使用模板函数进行交换Swap(a, b); Swap(x, y); Swap(c1, c2); std::cout << "交换后的整数: " << a << " " << b << std::endl;std::cout << "交换后的浮点数: " << x << " " << y << std::endl;std::cout << "交换后的字符: " << c1 << " " << c2 << std::endl;return 0;
}
输出示例:
交换后的整数: 20 10
交换后的浮点数: 2.2 1.1
交换后的字符: B A
1.2.4 模板中的typename与class
在定义模板时,typename和class是可以互换的。你可以选择以下两种方式:
template<typename T> // 使用 typename
template<class T> // 使用 class
虽然两者功能相同,但推荐使用typename,因为它能够更好地表达该参数是一个类型参数,避免与类的定义产生混淆。
1.3 函数模板的原理
函数模板的核心在于它不是一个真正的函数,而是一个编译器用来生成特定类型函数的蓝图。编译器根据模板的使用情况推导出具体的类型,并生成相应的代码。这一过程称为模板的实例化。
1.3.1 函数模板的实例化
当我们调用模板函数时,编译器会根据实际的参数类型生成对应的函数版本。比如:
template<class T>
T Add(const T& left, const T& right) {return left + right;
}int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;Add(a, b); // 生成 Add<int> 版本Add(x, y); // 生成 Add<double> 版本return 0;
}

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应
类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,
将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
1.3.2 隐式实例化与显式实例化
template<class T>T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);Add(a1, d1);/*
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有
一个T,
编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要
背黑锅*/// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(a1, (int)d1);//自己强制转换return 0;
}
模板实例化分为两种:
- 隐式实例化:编译器根据实参推导出模板参数,并自动生成函数。例如上面的
Add函数就是隐式实例化。 - 显式实例化:如果想要强制指定模板参数,可以使用显式实例化:
就是第二种处理方式
int main(void)
{int a = 10;double b = 20.0;// 显式实例化Add<int>(a, b);return 0;
};
第二章: 类模板
2.1 类模板概念
类模板(Class Template)是用于定义与类型无关的类,它允许我们在类的定义中使用模板参数,编译时再根据实际类型进行类的实例化。类模板与函数模板类似,只不过它是用来生成类的。
2.1.1 类模板的定义格式
定义类模板的语法格式如下:
template<class T>
class 类名 {// 类的成员变量和方法
};
template<class T>:告诉编译器这是一个模板类,T是一个类型参数。- 类的成员和方法可以使用
T作为数据类型,编译时由用户提供的类型来替代T。
2.1.2 示例:简单的类模板
下面是一个简单的栈(Stack)类模板,用于存储任意类型的数据:
#include<iostream>using namespace std;/*** @brief 栈的类模板* @tparam T 通用的类型参数*/
template<class T>
class Stack {
public:Stack(size_t capacity = 4) {_array = new T[capacity];_capacity = capacity;_size = 0;}~Stack() {delete[] _array;}/// @brief 将元素压入栈中void Push(const T& data) {if (_size == _capacity) {Expand(); // 扩容}_array[_size++] = data;}/// @brief 弹出栈顶元素void Pop() {if (_size > 0) {--_size;}}/// @brief 返回栈顶元素T& Top() {if (_size > 0) {return _array[_size - 1];}throw out_of_range("栈为空!");}/// @brief 检查栈是否为空bool IsEmpty() const {return _size == 0;}/// @brief 获取栈中元素的数量size_t Size() const {return _size;}private:T* _array;size_t _capacity;size_t _size;/// @brief 扩展栈的容量void Expand() {size_t newCapacity = _capacity * 2;T* newArray = new T[newCapacity];for (size_t i = 0; i < _size; ++i) {newArray[i] = _array[i];}delete[] _array;_array = newArray;_capacity = newCapacity;}
};int main() {Stack<int> st; // 创建存储整数的栈st.Push(10);st.Push(20);st.Push(30);cout << "栈顶元素: " << st.Top() << endl;st.Pop();cout << "弹出后栈顶元素: " << st.Top() << endl;return 0;
}
输出示例:
栈顶元素: 30
弹出后栈顶元素: 20
在这个类模板中,T是一个通用类型参数。Stack<int>是Stack类模板的一个实例化,表示它是一个存储int类型数据的栈。编译器会根据实际使用的类型自动生成相应的类。
2.2 类模板的实例化
与函数模板不同,类模板在使用时必须显示地提供类型参数。实例化类模板时,必须在类名后面的尖括号<>中指定实际的类型参数。例如:
Stack<int> st1; // 实例化为处理 int 类型的栈
Stack<double> st2; // 实例化为处理 double 类型的栈
这里的Stack<int>和Stack<double>分别表示不同的类型,即不同的模板实例。编译器会根据模板参数生成相应的类代码。
2.3 类模板中的成员函数定义
对于类模板,成员函数可以在类定义内或定义外实现。类模板的成员函数定义外置时,需要在函数名之前加上模板声明和模板参数。例如:
template<class T>
void Stack<T>::Push(const T& data) {if (_size == _capacity) {Expand(); // 扩容}_array[_size++] = data;
}
2.4 为什么不建议类模板的定义和声明分离?
在C++中,类模板的实现与普通类有一个显著的区别:模板是在编译时根据实际类型实例化的,而不是像普通的类那样在编译期和链接期处理。这导致了一个很重要的问题:如果将类模板的声明和定义分离到不同的文件中,可能会导致链接错误。以下是详细原因:
2.4.1 模板的编译时行为
类模板的本质是一个“蓝图”,它并不是一个完整的类,而是一个在需要时根据实际类型生成代码的模式。因此,模板只有在实际使用(实例化)时,编译器才会生成对应的类型的代码。编译器无法预先知道你会使用哪些类型来实例化模板,因此它不会为模板生成实际的代码。
2.4.2 链接器无法找到定义
当你将类模板的声明放在头文件中,而把定义放在.cpp文件中时,模板实例化的过程可能发生在不同的编译单元中。因为模板只有在编译期被实例化,链接器在链接时无法看到模板的定义,除非在编译时所有模板的实例化代码都可见。如果定义在.cpp文件中,其他使用模板的编译单元无法找到这个定义,导致链接器报错。
2.4.3 无法预编译模板
与普通类不同,类模板无法被预编译或只在一个编译单元中定义然后供其他单元使用。普通的类在编译过程中,编译器会生成目标代码并储存在.obj文件中,链接时其他编译单元可以引用这些已生成的代码。而类模板无法这样做,因为它需要知道使用时的类型才能生成实际的代码。
2.4.4 解决方案:将声明和定义放在同一个头文件中
为了避免上述问题,C++的惯用方法是将类模板的声明和定义都放在同一个头文件中。这使得每个使用模板的编译单元在实例化模板时,编译器能够访问到模板的定义,并根据需要生成实际的代码。这种方式确保了编译器能够在编译期处理模板的实例化,而不会在链接时出现找不到定义的问题。
错误用法:
// Stack.h
template<typename T>
class Stack {
public:void Push(const T& value);// 声明在头文件中
};// Stack.cpp
template<typename T>
void Stack<T>::Push(const T& value) {// 定义在.cpp文件中
}
在这种情况下,如果不同编译单元使用了Stack<int>或Stack<double>,链接器可能会报错,因为它无法找到模板的定义。
正确用法:
// Stack.h
template<typename T>
class Stack {
public:void Push(const T& value);
};template<typename T>
void Stack<T>::Push(const T& value) {// 声明和定义都在头文件中
}
这种方法确保每个编译单元都能访问到模板的完整定义,避免链接时的错误。
总结:
类模板的代码只有在实例化时才生成,因此类模板的定义必须在每个使用它的编译单元中可见。将模板的声明和定义放在同一个头文件中,可以确保模板实例化时能够访问到其定义,避免链接错误。这也是为什么大多数C++开发者在编写模板时会将模板的实现放在头文件中的原因。
注意:
函数模板与类模板不同,当代大多数编译器支持函数模板的声明和定义分离,这是因为函数模板的实例化往往只涉及函数的具体调用,不像类模板这么复杂,具体之后的博客会更详细的讲解此处的内容,敬请期待哦💕
第三章: 模板的匹配原则
3.1 模板的匹配原则
C++编译器在调用模板时,会根据实参类型和函数参数类型进行匹配。模板的匹配规则如下:
-
优先调用非模板函数:如果存在一个与实参完全匹配的非模板函数,编译器将优先调用非模板函数,而不是通过模板生成一个实例。
例如:
int Add(int a, int b) {return a + b; }template<typename T> T Add(T a, T b) {return a + b; }int main() {int a = 10, b = 20;cout << Add(a, b) << endl; // 调用非模板版本 } -
如果非模板函数没有匹配,则调用模板实例:如果模板函数比非模板函数更能匹配参数类型,编译器将生成模板实例。
例如:
double Add(double a, double b) {return a + b; }template<typename T> T Add(T a, T b) {return a + b; }int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;cout << Add(a, b) << endl; // 调用模板实例 Add<int>cout << Add(x, y) << endl; // 调用非模板版本 Add(double, double) }
写在最后
本文基础的讲解了C++模板编程的基础知识,涵盖了泛型编程、函数模板、类模板、模板匹配原则等概念。通过这些模板功能,C++开发者可以编写更加灵活和可复用的代码,大幅提高编程效率。
模板编程虽然强大,但使用时也需要谨慎,尤其是在处理模板特化和匹配规则时。如果能够合理地使用模板技术,相信你的代码质量将会有显著提升。
以上就是关于【C++篇】引领C++模板初体验:泛型编程的力量与妙用的内容啦,在之后会有另一篇博客来讲解有关模板的更多进阶内容,敬请期待哦,然后各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

相关文章:
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程?1.1.1 为什么要有泛型编程?1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板?1.2.2 函数模板的定义格式1.2.3 示例:通用的交换函数输出示例&am…...
在react中 使用redux
1.安装redux npm install reduxjs/toolkit react-redux 2.创建切片模块化数据 在Src目录下创建store目录,创建moude目录 创建tab.js import { createSlice } from reduxjs/toolkit; const tabSlice createSlice({name: tab,initialState: {Collapse: false,},re…...
计算机毕业设计python+spark知识图谱房价预测系统 房源推荐系统 房源数据分析 房源可视化 房源大数据大屏 大数据毕业设计 机器学习
《PythonSpark知识图谱房价预测系统》开题报告 一、研究背景与意义 随着城市化进程的加速和房地产市场的不断发展,房价成为影响人们生活质量的重要因素之一。准确预测房价不仅有助于政府制定科学的房地产政策,还能为开发商提供市场参考,同时…...
Spring-bean的生命周期-终篇
阶段8:Bean属性设置阶段 属性设置阶段分为3个小的阶段 实例化后阶段Bean属性赋值前处理Bean属性赋值 实例化后阶段 这里也有spring给我们预留了扩展,就是实现InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法,开发…...
Kotlin 枚举和 when 表达式(六)
导读大纲 1.1 表示和处理选择: Enums和when1.1.1 声明枚举类和枚举常量1.1.2 使用 when 表达式处理枚举类 1.1 表示和处理选择: Enums和when 在本节中,我们将以在 Kotlin 中声明枚举为例,介绍 when 结构 when可以被视为比 Java 中 switch 结构更强大、更常用的替代品 1.1.1 …...
数字范围按位与
优质博文:IT-BLOG-CN 题目 给你两个整数left和right,表示区间[left, right],返回此区间内所有数字 按位与 的结果(包含left、right端点)。 示例 1: 输入:left 5, right 7 输出:…...
WebRTC编译后替换libwebrtc.aar时提示找不到libjingle_peerconnection_so.so库
Loading native library: jingle_peerconnection_so 问题原因:编译的时候只编译了armeabi-v7a的版本,但是应用程序是arm64-v8a,所以无法运行 解决方法:更新编译脚本,加上arm64-v8a进行编译 ./tools_webrtc/android/bu…...
Nature Electronics |无感佩戴的纤维基电子皮肤(柔性半导体器件/柔性健康监测/电子皮肤/柔性传感/纤维器件)
英国剑桥大学Yan Yan Shery Huang课题组,在《Nature Electronics 》上发布了一篇题为“Imperceptible augmentation of living systems with organic bioelectronic fibres”的论文,第一作者为王文宇博士(Wenyu Wang),论文内容如下: 一、 摘要 利用电子技术对人类皮肤和…...
深入剖析Docker容器安全:挑战与应对策略
随着容器技术的广泛应用,Docker已成为现代应用开发和部署的核心工具。它通过轻量级虚拟化技术实现应用的隔离与封装,提高了资源利用率。然而,随着Docker的流行,其安全问题也成为关注焦点。容器化技术虽然提供了良好的资源隔离&…...
后端技术打怪升级之路
记录后端技术打怪升级之路,如下是个人总记的主要技术栈,仅供参考! 备注: 同名文章一同步发表于个人网站及微信公众号 个人网站 工藤新一的技术小窝...
Leetcode 3296. Minimum Number of Seconds to Make Mountain Height Zero
Leetcode 3296. Minimum Number of Seconds to Make Mountain Height Zero 1. 解题思路2. 代码实现 题目链接:3296. Minimum Number of Seconds to Make Mountain Height Zero 1. 解题思路 这一题的思路的话我们采用的是一个二分法的思路,找到一个最大…...
计算机毕业设计之:基于深度学习的路面检测系统(源码+部署文档+讲解)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
测试面试题:接口自动化测试流程?
1、测试用例编写:根据接口的需求和功能,编写相应的测试用例。测试用例应包括正常、边界和异常等各种情况下的测试。 2、准备测试数据:根据测试用例的要求,准备相应的测试数据。数据可以通过手动输入、数据库查询、文件导入等方式进…...
Golang面试题
在Golang(也称为Go语言)工程师的面试中,可能会遇到各种技术性和概念性的问题。 一、基础部分 Golang 中 make 和 new 的区别? 共同点:两者都用于分配内存。不同点: make 专为 slice、map 和 channel 设计,返回初始化后的(非零)值。new 分配内存并返回指向该内存的指针…...
《飞机大战游戏》实训项目(Java GUI实现)(设计模式)(简易)
目录 一、最终实现后,效果如下。 (1)简单介绍本游戏项目(待完善) (2)运行效果图(具体大家自己可以试) 初始运行情况。 手动更换背景图。 通过子弹攻击敌机,累…...
计算机毕业设计 基于 Hadoop平台的岗位推荐系统 SpringBoot+Vue 前后端分离 附源码 讲解 文档
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...
【数据结构与算法】LeetCode:二分查找
文章目录 二分查找二分查找搜索插入位置 (Hot 100)x 的平方根搜索二维矩阵(Hot 100)在排序数组中查找元素的第一个和最后一个位置 (Hot 100)搜索旋转排序数组 (Hot 100)寻找旋转排序…...
专题·大模型安全 | 生成式人工智能的内容安全风险与应对策略
正如一枚硬币的两面,生成式人工智能大模型(以下简称“生成式大模型”)在助力内容生成的同时也潜藏风险,成为虚假信息传播、数据隐私泄露等问题的温床,加剧了认知域风险。与传统人工智能(AI)相比…...
CORS跨域+Nginx配置、Apache配置
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种机制,它使用额外的HTTP头部来告诉浏览器允许一个网页运行的脚本从不同于它自身来源的服务器上请求资源(例如字体、JavaScript、CSS等)。这是一种安…...
文件查找和打包压缩【1.7】
文件查找和打包压缩【1.7】 八、文件查找和打包压缩8.1 文件查找8.1.1 locate8.1.2 findfind8.1.2.1 指定搜索目录层级8.1.2.2 先处理文件再处理目录8.1.2.3 根据文件名和inode查找8.1.2.4 根据属主属组查找8.1.2.5 根据文件类型查找8.1.2.6 空文件或目录8.1.2.7 组合条件8.1.2…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
