《 C++ 点滴漫谈: 三十四 》从重复到泛型,C++ 函数模板的诞生之路
一、引言
在 C++ 编程的世界里,类型是一切的基础。我们为 int
写一个求最大值的函数,为 double
写一个相似的函数,为 std::string
又写一个……看似合理的行为,逐渐堆积成了难以维护的 “函数墙”。这些函数逻辑几乎一致,仅仅是参数类型不同,却不得不反复实现。这种 “代码冗余” 是传统 C 语言开发中普遍存在的问题。
为了彻底解决这一难题,C++ 提出了 “模板编程”(Template Programming)这一强大机制,其中最基础也最常用的,就是函数模板(Function Template)。它允许我们写出与类型无关的函数逻辑,编译器会在调用时根据传入的类型生成对应的函数版本,从而实现 “一次编写,多处复用” 的理想目标。
函数模板不仅极大地提高了代码复用率,还成为 C++ 泛型编程(Generic Programming)的基石,是构建现代 C++ 标准库(如 STL)中不可或缺的核心工具。你所熟知的 std::sort
、std::swap
、std::max
等,其实都是函数模板的杰出代表。
此外,随着 C++11、C++14、C++17 乃至 C++20 的演进,函数模板的语法和功能也不断增强,从支持 auto
类型推导、decltype
辅助判断,到引入 if constexpr
和 Concepts 等高级特性,使得模板不仅易用,而且更强大、更安全、更灵活。
在这篇文章中,我们将从最基础的函数模板语法出发,深入探讨其背后的类型推导机制、与普通函数的协作方式、特化技巧,以及与现代 C++ 特性的完美结合。同时,我们还将结合真实项目中的案例,解析函数模板在工程实践中的作用与价值,帮助你真正掌握这一泛型编程的利器。
二、函数模板基础语法
C++ 中的函数模板(Function Template)是一种可以处理不同类型参数的函数定义方式,是泛型编程的核心工具。通过模板机制,开发者可以将函数逻辑抽象成与 “类型” 无关的通用形式,由编译器根据调用时的实际类型自动生成对应的函数版本,从而提高代码的复用性与可维护性。
2.1、函数模板的基本语法
函数模板的声明和定义通常使用以下语法结构:
template<typename T>
返回类型 函数名(参数列表) {// 函数体
}
template
是关键字,标志我们正在定义一个模板。typename T
表示类型参数 T,T 可以是任意的类型。- 也可以使用
class T
,与typename T
在此上下文中是完全等价的。
- 也可以使用
- T 可以在函数参数、返回值中使用,也可以在函数体中使用。
🔸 示例:一个通用的 swap
函数
#include <iostream>
using namespace std;template<typename T>
void mySwap(T& a, T& b) {T temp = a;a = b;b = temp;
}int main() {int x = 10, y = 20;mySwap(x, y);cout << "x = " << x << ", y = " << y << endl;double p = 3.14, q = 2.71;mySwap(p, q);cout << "p = " << p << ", q = " << q << endl;return 0;
}
运行结果:
x = 20, y = 10
p = 2.71, q = 3.14
编译器根据不同类型自动生成对应的函数版本,大大减少了重复代码。
2.2、多类型模板参数
函数模板不仅支持一个类型参数,还可以使用多个类型参数来适应更复杂的函数签名:
template<typename T1, typename T2>
void printPair(T1 a, T2 b) {std::cout << "First: " << a << ", Second: " << b << std::endl;
}int main() {printPair(42, "Hello");printPair(3.14, true);
}
输出:
First: 42, Second: Hello
First: 3.14, Second: 1
2.3、模板函数的调用方式
✅ 自动类型推导
编译器会根据实参自动推导模板参数类型:
mySwap(x, y); // 自动推导 T = int
✅ 显式指定模板参数
有时候推导失败或不够明确,可以手动指定类型:
mySwap<int>(x, y); // 显式指定 T 为 int
这对于有类型转换或歧义的情况非常有用。
2.4、typename
和 class
的区别?
在函数模板的定义中,template<typename T>
与 template<class T>
是完全等价的。两者只是语义上的不同,C++ 标准推荐使用 typename
来强调这是一个 “类型参数”,而不是一个类。以下两个写法效果一致:
template<typename T> void func1(T val); // 推荐
template<class T> void func2(T val); // 等价
2.5、模板函数不能自动实例化为所有类型
虽然模板非常强大,但它不是 “魔法函数工厂”。如果模板内部对类型 T
做了某些操作(如使用操作符 <
),那么该类型必须支持该操作:
template<typename T>
bool compare(T a, T b) {return a < b; // T 必须支持 operator<
}
如果你对一个不支持 <
的类型调用此模板,会在编译阶段报错,这就是 模板实例化错误 的一部分。
2.6、小结小贴士 ✅
要点 | 内容 |
---|---|
template<typename T> | 声明一个函数模板 |
多类型参数 | 使用 template<typename T1, typename T2> |
类型推导 | 自动进行,也可显式指定 |
使用限制 | 类型 T 必须支持模板中涉及的操作 |
class vs typename | 没有本质区别,推荐用 typename |
函数模板是 C++ 中泛型编程的起点,它让我们写出类型无关的逻辑。掌握了基本语法后,我们将在下一节继续探索模板的 类型推导机制、模板与普通函数之间的互动规则,逐步走向更高级的使用方式。
三、模板的类型推导与显式指定
在上一节中,我们学习了函数模板的基本语法。真正让模板函数强大和灵活的,是 C++ 的 类型推导机制(Type Deduction),以及支持开发者手动 显式指定模板参数(Explicit Specification) 的能力。
本节将从规则、细节和陷阱出发,全面揭示模板类型推导与显式指定的底层逻辑。
3.1、什么是类型推导?
当我们调用一个函数模板时,如果没有显式指定模板参数,编译器会根据函数参数自动推导出模板类型。
示例:
template<typename T>
void print(T value) {std::cout << "Value: " << value << std::endl;
}int main() {print(42); // T 被推导为 intprint(3.14); // T 被推导为 doubleprint("Hello"); // T 被推导为 const char*
}
推导发生在编译期间,编译器根据参数类型生成对应版本的函数定义。
3.2、显式指定模板参数
开发者也可以在调用函数模板时,明确地指定模板参数类型,这种做法被称为显式指定:
print<int>(42); // 显式指定 T 为 int
print<double>(42); // 显式将 42 转为 double
这在某些类型无法正确推导,或需要强制转换的场景中尤为重要。
3.3、推导的限制与陷阱
虽然 C++ 的类型推导很强大,但也存在一些限制和 “坑”:
✅ 引用与 const 的推导规则
template<typename T>
void func(T arg); // T 是值传递int x = 10;
const int y = 20;
func(x); // T 推导为 int
func(y); // T 推导为 int(不是 const int)
- 值传递会去掉引用和 const 修饰符。
- 若要保持引用类型,需显式声明为引用参数:
template<typename T>
void func_ref(T& arg); // T 是引用func_ref(x); // T 推导为 int,参数类型为 int&
func_ref(y); // T 推导为 const int,参数类型为 const int&
✅ 数组、指针类型的推导
template<typename T>
void showSize(T arg) {std::cout << sizeof(arg) << std::endl;
}int arr[10];
showSize(arr); // T 推导为 int*,数组退化为指针
- 数组作为函数参数会退化为指针,需要使用引用以保持数组大小:
template<typename T, size_t N>
void showArray(T (&arr)[N]) {std::cout << "Array size: " << N << std::endl;
}
3.4、函数模板参数与非模板参数混用
C++ 允许模板函数中混合使用 模板参数 与 非模板参数:
template<typename T>
void fillArray(T value, int count) {for (int i = 0; i < count; ++i)std::cout << value << " ";std::cout << std::endl;
}
其中 T
是模板参数,而 count
是普通的 int
类型参数。
3.5、模板参数不能从返回值推导
函数模板只能从参数列表推导类型,返回值不参与类型推导:
template<typename T>
T identity() {return T();
}int x = identity(); // 错误!不能推导出 T
int y = identity<int>(); // 正确,显式指定 T 为 int
3.6、多个参数的推导规则
当模板函数有多个模板参数时,每个参数都可能有不同的推导规则:
template<typename T1, typename T2>
void showPair(T1 a, T2 b);showPair(1, 2.0); // T1=int, T2=double
showPair("Hi", 'a'); // T1=const char*, T2=char
若参数类型之间存在冲突,例如传入 (int, int)
但显式指定为 (T, T*)
则会导致编译错误。
3.7、默认模板参数(C++11 起)
C++11 起允许为函数模板提供默认模板参数:
template<typename T = int>
void printDefault(T value) {std::cout << value << std::endl;
}printDefault(123); // 推导为 int
printDefault<double>(3.14); // 显式指定为 double
3.8、结合 auto
与模板推导(C++14/17)
C++14 起允许函数返回类型为 auto
,并通过模板参数推导:
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {return a + b;
}
C++17 起支持更简洁写法:
template<typename T1, typename T2>
auto add(T1 a, T2 b) {return a + b;
}
3.9、小结对照表:类型推导中的常见规则
场景 | 推导结果 | 是否保留修饰符 |
---|---|---|
值传递 | 去掉 const、引用 | ❌ 否 |
引用传递 | 保留 const、引用 | ✅ 是 |
数组传参 | 退化为指针 | ❌ 否 |
返回值 | 不参与推导 | ❌ 否 |
显式指定 | 手动确定类型 | ✅ 是 |
- 类型推导是函数模板的核心机制,编译器会根据参数自动分析模板类型。
- 推导会移除
const
和引用,数组会退化为指针,注意这些 “隐性转换”。 - 当推导失败或有歧义时,显式指定模板参数是最佳方式。
- C++11 之后支持默认模板参数、
decltype
、返回类型推导等新特性,极大提升模板的表达力。
四、模板函数与普通函数的共存与重载
在 C++ 中,函数模板为通用编程提供了强大工具,但这并不意味着它们会取代所有普通函数。在实际开发中,我们常常希望模板函数和普通函数共存,并且根据不同的参数类型进行自动的重载选择。
这一节将深入探讨模板函数与普通函数如何相互协作,包括它们的优先级机制、匹配细节以及开发中常见的陷阱。
4.1、模板函数与普通函数可以共存吗?
答案是:可以,并且它们之间可以自由重载。
当一个函数模板与一个普通函数同名且参数形式相似时,编译器会优先选择与调用参数最匹配的函数版本。优先级的顺序如下:
- 完全匹配的普通函数(non-template)
- 可匹配的模板函数(template)
- 更特化的模板函数(partial specialization,见后节)
4.2、基本示例:普通函数优先
#include <iostream>template<typename T>
void print(T value) {std::cout << "Template: " << value << std::endl;
}void print(int value) {std::cout << "Normal: " << value << std::endl;
}int main() {print(10); // 调用普通函数print("Hello"); // 调用模板函数
}
输出:
Normal: 10
Template: Hello
- 对于
print(10)
,普通函数print(int)
更匹配,因此被优先选择。 - 对于
print("Hello")
,没有对应的普通函数,因此选择模板版本。
4.3、模板函数之间的重载
模板函数之间也可以重载,例如根据参数个数或参数类型:
template<typename T>
void show(T x) {std::cout << "One parameter: " << x << std::endl;
}template<typename T1, typename T2>
void show(T1 x, T2 y) {std::cout << "Two parameters: " << x << ", " << y << std::endl;
}int main() {show(10); // 匹配第一个模板show(3.14, "Pi"); // 匹配第二个模板
}
4.4、模板与普通函数重载的歧义
有时,模板函数与普通函数的匹配度可能相近,从而引发编译器 “歧义错误”:
template<typename T>
void func(T a) {std::cout << "Template func" << std::endl;
}void func(double a) {std::cout << "Non-template func" << std::endl;
}int main() {func(3.14f); // float 类型对模板和普通函数都可以匹配
}
结果:
float
能被转换为double
,也能推导出T = float
- 若匹配度接近,可能出现模棱两可的情况,需要显式指定
4.5、如何解决歧义?
✅ 显式指定模板参数
func<float>(3.14f); // 明确选择模板版本
✅ 添加模板专属参数或限制
使用 std::enable_if
或 concepts
限制模板参数范围,以避免被普通函数抢先匹配(C++11/C++20):
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
func(T a) {std::cout << "Integral only" << std::endl;
}
4.6、与函数默认参数配合使用
如果普通函数带有默认参数,而模板函数没有,可能导致模板意外落后:
void hello(int x = 10) {std::cout << "hello(int)" << std::endl;
}template<typename T>
void hello(T t) {std::cout << "hello(T)" << std::endl;
}int main() {hello(); // hello(int)
}
4.6、模板函数不能重载只靠返回值区分
返回值类型不参与重载决议,因此以下代码非法:
template<typename T>
T convert(int value);template<typename T>
double convert(int value); // 错误:仅返回值不同
4.7、推荐实践
场景 | 推荐方式 |
---|---|
有具体类型的函数实现需求 | 使用普通函数 |
泛型实现、类型未知 | 使用模板函数 |
模板与普通函数同时存在 | 确保参数签名不同 |
模板选择性启用 | 使用 std::enable_if 或 concept 限制 |
✨ 小结
- 模板函数和普通函数可以共存,编译器会根据 “匹配度优先” 规则进行选择。
- 普通函数优先,模板是备选项;但在参数不匹配时,模板能提供兜底能力。
- 避免歧义的关键在于:区分参数类型、个数或借助 SFINAE 技术屏蔽某些模板实例化路径。
- 利用 C++11/14/20 的新特性可以更精确地控制重载行为。
五、模板特化与偏特化
在实际开发中,虽然函数模板通过泛型机制实现了代码复用,但有时我们仍希望为特定类型编写专门的函数实现,这就是模板特化(Template Specialization)与偏特化(Partial Specialization)发挥作用的地方。
5.1、什么是模板特化?
模板特化是指为特定类型的参数提供专门的模板实现。C++ 支持类模板和函数模板的特化,但注意:函数模板不能进行偏特化,只能进行全特化。而类模板则两者都支持。
5.2、函数模板的全特化(Function Template Specialization)
🔹 定义形式
template<typename T>
void print(T value); // 通用模板// 特化版本
template<>
void print<int>(int value) {std::cout << "int: " << value << std::endl;
}
🔹 使用示例
#include <iostream>template<typename T>
void print(T value) {std::cout << "Generic: " << value << std::endl;
}template<>
void print<int>(int value) {std::cout << "Specialized for int: " << value << std::endl;
}int main() {print(42); // 调用特化版本print(3.14); // 调用通用模板print("Hello"); // 调用通用模板
}
✅ 输出:
Specialized for int: 42
Generic: 3.14
Generic: Hello
✅ 特化的版本完全替代了通用模板在该类型上的实现,具有最高优先级。
5.3、偏特化是啥?为什么函数模板不能偏特化?
🔹 偏特化(Partial Specialization)
偏特化是指只对部分模板参数或部分类型结构进行特化处理,是类模板的一种强大功能。
template<typename T1, typename T2>
class Pair;// 偏特化版本:当第二个类型是 int
template<typename T1>
class Pair<T1, int> {
public:void show() {std::cout << "Second type is int" << std::endl;}
};
🚫 函数模板不能进行偏特化,因为编译器无法根据调用上下文唯一选择匹配度最高的偏特化版本,会导致二义性。
5.4、类模板偏特化的典型使用场景
示例:对不同类型的处理逻辑不同
#include <iostream>template<typename T>
struct TypeTrait {static void print() {std::cout << "Generic type" << std::endl;}
};// 偏特化:指针类型
template<typename T>
struct TypeTrait<T*> {static void print() {std::cout << "Pointer type" << std::endl;}
};int main() {TypeTrait<int>::print(); // 输出:Generic typeTypeTrait<int*>::print(); // 输出:Pointer type
}
✅ 类模板的偏特化让我们能够以结构性方式区分类型特征、启用不同实现策略,这是泛型编程中的重要技巧。
5.5、函数模板的伪偏特化方案
虽然函数模板不能偏特化,但我们可以借助类模板的偏特化 + 函数封装间接实现类似效果。
template<typename T>
struct PrintHelper {static void print(T value) {std::cout << "Generic: " << value << std::endl;}
};template<>
struct PrintHelper<int> {static void print(int value) {std::cout << "Specialized for int: " << value << std::endl;}
};template<typename T>
void print(T value) {PrintHelper<T>::print(value);
}int main() {print(100); // 特化版本print(3.14); // 通用版本
}
5.6、模板特化的小细节
细节点 | 说明 |
---|---|
函数模板只能全特化 | 无法偏特化,使用类模板辅助 |
模板参数顺序要一致 | 特化模板时需完全匹配原始模板参数结构 |
特化版本不会自动继承默认参数 | 必须重新定义所有默认参数 |
特化优先级最高 | 编译器会优先选择完全特化版本,而不是通用模板或普通重载函数 |
5.7、现代 C++ 特化替代方案:if constexpr
与 concepts
自 C++17 起,引入了 if constexpr
可在编译期实现类型判断逻辑,从而在模板函数中内联不同类型的实现分支。
template<typename T>
void print(T value) {if constexpr (std::is_integral<T>::value) {std::cout << "Integral type: " << value << std::endl;} else {std::cout << "Other type: " << value << std::endl;}
}
✅ 这是一种现代、高效、无须额外特化的做法,推荐用于轻量逻辑分支。
5.8、小结建议
需求 | 推荐做法 |
---|---|
为某类型提供完全不同实现 | 使用函数模板的全特化 |
为某类类型(如指针、整型)提供差异行为 | 使用类模板偏特化 |
想让函数模板支持结构差异 | 类模板偏特化 + 函数封装 |
仅需少量分支 | 使用 if constexpr 或 concepts |
✅ 小结
- 函数模板只能进行全特化,不能偏特化。
- 类模板可以进行偏特化,非常适合设计策略类、类型特征提取等。
- 模板特化是泛型编程的高级技巧,允许你兼顾“通用性”与“定制性”。
- 结合
if constexpr
和concepts
,可以更现代化地表达“特化行为”。
相关文章:
《 C++ 点滴漫谈: 三十四 》从重复到泛型,C++ 函数模板的诞生之路
一、引言 在 C 编程的世界里,类型是一切的基础。我们为 int 写一个求最大值的函数,为 double 写一个相似的函数,为 std::string 又写一个……看似合理的行为,逐渐堆积成了难以维护的 “函数墙”。这些函数逻辑几乎一致࿰…...
EasyRTC打造无人机低延迟高清实时通信监控全场景解决方案
一、方案背景 随着无人机技术的飞速发展,其在航拍、物流配送、农业监测、应急救援等多个领域的应用日益广泛。然而,无人机在实际作业过程中面临着诸多挑战,如通信延迟、数据传输不稳定、监控范围有限等。EasyRTC作为一种高效、低延迟的实时通…...
【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法(多输入单输出)
【MATLAB第117期】#源码分享 | 基于MATLAB的SSM状态空间模型多元时间序列预测方法(多输入单输出) 引言 本文使用状态空间模型实现失业率递归预测,状态空间模型(State Space Model, SSM)是一种用于描述动态系统行为的…...
关于大数据的基础知识(三)——数据安全与合规
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于大数据的基础知识(三&a…...
从信息泄露到内网控制
0x01 背景 之前常见用rce、文件上传等漏洞获取webshell,偶然遇到一次敏感信息泄露获取权限的渗透,简单记录一下过程。 0x02 信息泄露 发现系统某端口部署了minio服务,经过探测发现存在minio存储桶遍历 使用利用工具把泄露的文件全部整理一…...
【Qt】QDialog类
🌈 个人主页:Zfox_ 🔥 系列专栏:Qt 目录 一:🔥 对话框 - QDialog 🦋 基本介绍🦋 对话框分类🦋 Qt 内置对话框🎀 QMessageBox -消息对话框🎀 QColo…...
【Spring Boot基础】MyBatis的基础操作:增删查改、列名和属性名匹配 -- XML实现
MyBatis的基础操作 1. MyBatis XML配置文件1.1 简单介绍1.2 配置连接字符串和MyBatis1.3 XMl文件实现--分层1.4 XMl文件实现--举例 2.增删改查操作2.1 增(insert)2.1.1 不使用Param2.1.2 用Param2.1.3 返回自增键 2.2 删(delete)2…...
谷歌推出探索型推荐新范式:双LLM架构重塑用户兴趣挖掘
文章目录 1. 背景1.1 闭环困境1.2 谷歌的两次失败尝试1.2.1 尝试一:轻量微调1.2.2 尝试二:RLHF 强化学习微调 1.3 双LLM范式的提出1.3.1 模型1:Novelty LLM — 负责生成“探索方向”1.3.2 模型2:Alignment LLM — 负责评估“相关性…...
Linux kernel signal原理(下)- aarch64架构sigreturn流程
一、前言 在上篇中写到了linux中signal的处理流程,在do_signal信号处理的流程最后,会通过sigreturn再次回到线程现场,上篇文章中介绍了在X86_64架构下的实现,本篇中介绍下在aarch64架构下的实现原理。 二、sigaction系统调用 #i…...
使用 LangChain + Higress + Elasticsearch 构建 RAG 应用
RAG(Retrieval Augmented Generation,检索增强生成) 是一种结合了信息检索与生成式大语言模型(LLM)的技术。它的核心思想是:在生成模型输出内容之前,先从外部知识库或数据源中检索相关信息&…...
【Linux】46.网络基础(3.3)
文章目录 5. 其他重要协议或技术5.1 DNS(Domain Name System)5.1.1 DNS背景5.1.2 域名简介 5.2 ICMP协议5.2.1 ICMP功能5.2.2 ICMP的报文格式5.2.3 ping命令5.2.4 一个值得注意的坑5.2.5 traceroute命令 5.3 NAT技术5.3.1 NAT技术背景5.3.2 NAT IP转换过程5.3.3 NAPT5.3.4 NAT技…...
【Unity笔记】Unity + OpenXR项目无法启动SteamVR的排查与解决全指南
图片为AI生成 一、前言 随着Unity在XR领域全面转向OpenXR标准,越来越多的开发者选择使用OpenXR来构建跨平台的VR应用。但在项目实际部署中发现:打包成的EXE程序无法正常启动SteamVR,或者SteamVR未能识别到该应用。本文将以“Unity OpenXR …...
【sylar-webserver】重构 增加内存池
文章目录 内存池设定结构ThreadCacheCentralCachePageCache allocatedeallocate测试 参考 https://github.com/youngyangyang04/memory-pool 我的代码实现见 https://github.com/star-cs/webserver 内存池 ThreadCache(线程本地缓存) 每个线程独立的内存…...
云账号安全事件分析:黑客利用RAM子账户发起ECS命令执行攻击
事件背景 某企业云监控系统触发高危告警,提示API请求中包含黑客工具特征(cf_framework),攻击者试图通过泄露的RAM子账户凭据调用ECS高危API。以下是攻击关键信息整理: 字段详情告警原因API请求包含黑客工具特征(cf_framework)攻击实体RAM子账户 mq泄露凭证AccessKey ID…...
Node.js 模块导入的基本流程
Node.js 模块导入的基本流程,主要是 CommonJS 模块加载机制(即使用 require())的内部执行步骤。下面我用清晰的结构给你梳理一下这个过程: ✅ Node.js 模块导入的基本流程(使用 require()) const someModu…...
Unitest和pytest使用方法
unittest 是 Python 自带的单元测试框架,用于编写和运行可重复的测试用例。它的核心思想是通过断言(assertions)验证代码的行为是否符合预期。以下是 unittest 的基本使用方法: 1. 基本结构 1.1 创建测试类 继承 unittest.TestC…...
wps批量修改字体
选择这个小箭头 找到需要修改的字体如正文,右击修改选择合适的字体确定即可...
【Linux网络】各版本TCP服务器构建 - 从理解到实现
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
航电系统多模态融合技术要点
一、技术要点 1. 多模态数据特性分析 异构数据对齐:需处理不同传感器(如雷达、摄像头、IMU、ADS-B等)在时间、空间、精度和采样率上的差异,需设计同步机制(如硬件时钟同步、软件插值对齐)。 数据预处…...
【Git】branch合并分支
在 Git 中,将分支合并到 main 分支是一个常见的操作。以下是详细的步骤和说明,帮助你完成这个过程。 1. 确保你在正确的分支上 首先,你需要确保当前所在的分支是 main 分支(或者你要合并到的目标分支)。 检查当前分支…...
uniapp-商城-33-shop 布局搜索页面以及u-search
shop页面上有一个搜索,可以进行商品搜索,这里我们先做一个页面布局,后面再来进行数据i联动。 1、shop页面的搜索 2、搜索的页面代码 <navigator class"searchView" url"/pagesub/pageshop/search/search"> …...
蓝桥杯常考的找规律题
目录 灵感来源: B站视频链接: 找规律题具有什么样的特点: 报数游戏(Java组): 题目描述: 题目链接: 思路详解: 代码详解: 阶乘求和(Java组…...
全球化2.0 | 云轴科技ZStack亮相2025香港国际创科展
4月13-16日,由香港特别行政区政府、香港贸发局主办的2025香港国际创科展(InnoEX)在香港会议展览中心举办,作为亚洲最具影响力的科技盛会之一,本届展会吸引了来自17个国家和地区的500余家顶尖科技企业、科研机构及行业先…...
【Python进阶】数据可视化:Matplotlib从入门到实战
Python数据可视化:Matplotlib完全指南 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现案例1:折线图(股票趋势&#…...
操作系统——堆与栈详解:内存结构全面科普
文章目录 堆与栈详解:内存结构全面科普一、程序内存结构总览二、各段介绍及特点1. 代码段 .text2. 数据段 .data3. BSS段 .bss4. 堆区 Heap5. 栈区 Stack 三、C语言实例分析四、深入理解:为什么堆空间可能不连续?1. 堆内部结构:链…...
Mysql面试知识点详解
Mysql面试知识点详解 Mysql 是 Java 开发领域中常用的持久层框架,在面试和实际开发中都占据重要地位。本文将深入剖析 Mysql的核心知识点,并结合实战案例,帮助读者全面掌握相关技能。 一、慢查询定位与分析 (一)定位…...
数智读书笔记系列030《曲折的职业道路:在终身工作时代找准定位》与《做自己的教练:战胜工作挑战掌控职业生涯》
书籍简介 《曲折的职业道路:在终身工作时代找准定位》由英国职业发展专家海伦塔珀(Helen Tupper)和莎拉埃利斯(Sarah Ellis)合著,旨在帮助读者应对现代职场中日益普遍的“非直线型”职业路径。两位作者是“神奇的如果”(Amazing If)公司的联合创始人,曾为李维斯、沃达…...
Linux内核之文件驱动随笔
前言 近期需要实现linux系统文件防护功能,故此调研了些许知识,如何实现文件防护功能从而实现针对文件目录防护功能。当被保护的目录,禁止增删改操作。通过内核层面实现相关功能,另外在通过跟应用层面交互从而实现具体的业务功能。…...
【python】如何将文件夹及其子文件夹下的所有word文件汇总导出到一个excel文件里?
根据你的需求,这里提供一套完整的Python解决方案,支持递归遍历子文件夹、提取Word文档内容(段落+表格),并整合到Excel中。以下是代码实现及详细说明: 一个单元格一个word的全部内容 完整代码 # -*- coding: utf-8 -*- import os from docx import Document import pand…...
IDEA中如何统一项目名称/复制的项目如何修改根目录名称
1、问题概述? 在开发中,有时候为了方便,我们会复制一个新的项目,结果出现如下提示: 会在工程的后面提示工程原来的名字。 这种情况就是复制之后名字修改不彻底造成的。 2、彻底的修改工程的名字 2.1、修改pom.xml中…...
Ubuntu-Linux中vi / vim编辑文件,保存并退出
1.打开文件 vi / vim 文件名(例: vim word.txt )。 若权限不够,则在前方添加 sudo (例:sudo vim word.txt )来增加权限; 2.进入文件,按 i 键进入编辑模式。 3.编辑结…...
如何在idea里创建注释模版
✅ 步骤:创建一个类注释的 Live Template(缩写为 cls) ① 打开设置 IDEA 菜单栏点击:File > Settings(或按快捷键 Ctrl Alt S) ② 进入 Live Templates 设置 在左侧菜单找到:Editor > …...
IntelliJ IDEA 新版本中 Maven 子模块不显示的解决方案
一、问题现象与背景 在使用 IntelliJ IDEA 2024 版本开发 Maven 多模块项目时,我发现一个令人困惑的现象:父模块的子模块未在右侧 Maven 工具窗口中显示,仅显示父模块名称(且无 (root) 标识)。而此前在 IntelliJ IDEA…...
day48—双指针-通过删除字母匹配到字典最长单词(LeetCode-524)
题目描述 给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字…...
美乐迪电玩大厅加载机制与 RoomList 配置结构分析
本篇为《美乐迪电玩全套系统搭建》系列的第三篇,聚焦大厅与子游戏的动态加载机制,深入解析 roomlist.json 的数据结构、解析流程、入口配置方式与自定义接入扩展技巧。通过本篇内容,开发者可实现自由控制子游戏接入与分发策略。 一、RoomList…...
局域网内,将linux(Ubuntu)的硬盘映射成Windows上,像本地磁盘一样使用
如何把同处一个局域网内的Ubuntu硬盘,映射到Windows上,使得Windows就像使用本地磁盘一样使用Ubuntu的磁盘? 要在同一局域网内的Windows上像本地磁盘一样使用Ubuntu硬盘,可以按照以下步骤操作: 1. 在Ubuntu上设置Samba…...
界面控件DevExpress WPF v25.1预览 - 支持Windows 11系统强调色
DevExpress WPF拥有120个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…...
【Hive入门】Hive架构与组件深度解析:从核心组件到生态协同
目录 1 Hive架构全景图 2 核心组件运维职责详解 2.1 Metastore元数据中心 2.2 Driver驱动组件 2.3 Executor执行引擎 3 与HDFS/YARN的协同关系 3.1 HDFS协同架构 3.2 YARN资源调度 4 运维实战案例 4.1 Metastore连接泄露 4.2 小文件合并 5 最佳实践总结 5.1 性能优…...
【图像识别改名】如何批量识别多个图片的区域内容给图片改名,批量图片区域文字识别改名,基于WPF和腾讯OCR的实现方案和步骤
基于WPF和腾讯OCR的批量图像区域文字识别改名方案 本方案适用于以下场景: 大量扫描文档需要根据文档中的特定区域内容(如编号、标题等)进行重命名证件照片需要根据证件号码或姓名进行整理归档企业档案管理需要根据文件上的编号自动分类教育机构需要根据学生试卷上的学号自动…...
从ChatGPT到GPT-4:大模型如何重塑人类认知边界?
从ChatGPT到GPT-4:大模型如何重塑人类认知边界? 在人工智能(AI)领域,近年来最引人注目的进展之一是大型语言模型的发展。从最初的GPT-1到现在的GPT-4,这些模型不仅在技术上取得了显著的进步,而…...
QEMU源码全解析 —— 块设备虚拟化(21)
接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(20) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! 上一回开始解析blockdev_init函数,讲到了其中调用的blk_new_open函数,该函数的作用…...
vue2练习项目 家乡特色网站—前端静态网站模板
最近一直在学习前端 vue2 开发,基础知识已经学习的差不多了,那肯定需要写几个项目来练习一下自己学习到的知识点。今天就分享一个使用 vue2 开发的一个前端静态网站,【家乡特色网站】 先给大家看一下网站的样式: 这里就只简单的…...
CFIS-YOLO:面向边缘设备的木材缺陷检测轻量级网络解析
论文地址:https://arxiv.org/pdf/2504.11305 目录 一、论文核心贡献 二、创新点详解 2.1 CARAFE动态上采样 工作原理 优势对比 2.2 C2f_FNB轻量模块 计算效率 2.3 Inner-SIoU损失函数 三、实验验证 3.1 消融实验 3.2 对比实验 四、应用部署 4.1 边缘设备部署流程…...
vue3 + element-plus中el-dialog对话框滚动条回到顶部
对话框滚动条回到顶部 1、需要对话框显示后 2、使用 nextTick 等待 Dom 更新完毕 3、通过开发者工具追查到滚动条对应的标签及class“el-overlay-dialog”。追查方法: 4、设置属性 scrollTop 0 或者 执行方法 scrollTo(0, 0) // 对话框显示标识 const dialogVi…...
赛灵思Xilinx FPGa XCKU15P‑2FFVA1156I AMD Kintex UltraScale+
XCKU15P‑2FFVA1156I 是 AMD Kintex UltraScale 系列中的高性能 FPGA,基于 16 nm FinFET UltraScale 架构 制造,兼顾卓越的性能与功耗比,该器件集成 1,143,450 个逻辑单元和 82,329,600 位片上 RAM,配备 1,968 个 DSP 切片&#…...
力扣2492:并查集/dfs
方法一:并查集。如果不仔细读题,可能会想着把每一个一维数组下标为2的位置进行排序即可。但这是不行的。因为有可能有一些节点在其它集合当中。如果这些节点之间存在一个边权值比节点1所在集合的最小边权值还要小,那么求出来的答案就是错的。…...
宝塔面板引发的血案:onlyoffice协作空间无法正常安装的案例分享
今天和客户一起解决:onlyoffice协作空间的安装问题,本来已经发现由于客户用的机械硬盘,某些安装步骤等待的时间不够,已经加了处理。但是安装成功后,登录系统一直提示报错如下 检查docker容器都是正常的,并且health也是正确的,登录就一直报错。后面发现用免费版的安装程序可以正…...
【阿里云大模型高级工程师ACP习题集】2.1 用大模型构建新人答疑机器人
练习题 【单选题】1. 在调用通义千问大模型时,将API Key存储在环境变量中的主要目的是? A. 方便在代码中引用 B. 提高API调用的速度 C. 增强API Key的安全性 D. 符合阿里云的规定 【多选题】2. 以下哪些属于大模型在问答场景中的工作阶段?( ) A. 输入文本分词化 B. Toke…...
C++中的算术转换、其他隐式类型转换和显示转换详解
C中的类型转换(Type Conversion)是指将一个数据类型的值转换为另一个数据类型的过程,主要包括: 一、算术类型转换(Arithmetic Conversions) 算术类型转换通常发生在算术运算或比较中,称为**“标…...
Python自动化selenium-一直卡着不打开浏览器怎么办?
Python自动化selenium 如果出现卡住不打开,就把驱动放当前目录并指定 from selenium import webdriver from selenium.webdriver.chrome.service import Service import time import osdef open_baidu():# 获取当前目录中的chromedriver.exe的绝对路径current_di…...