C++函数模板:从泛型编程到编译时多态的实战指南
1. 项目概述为什么我们需要模板如果你写过C肯定遇到过这样的场景你需要一个函数来比较两个整数的大小于是你写了一个max(int a, int b)。过一会儿你又需要比较两个浮点数于是你复制粘贴代码改了个类型写了个max(float a, float b)。接着是double、long甚至是你自定义的MyClass对象假设它重载了运算符。很快你的代码里就充满了功能完全相同、仅仅是类型不同的函数。这不仅让代码变得臃肿更可怕的是维护成本——当你发现比较逻辑有个小bug时你得把所有重载函数都改一遍。这就是“模板”要解决的核心问题代码复用但不是在运行时而是在编译时。模板允许你编写与类型无关的通用代码编译器会根据你实际使用的类型自动生成对应的特化版本。这听起来有点像“宏”但模板是类型安全的并且是图灵完备的能力远超简单的文本替换。今天要聊的Th4.1指的就是C模板中最基础、最核心的部分函数模板。我会带你从“为什么需要它”开始彻底搞懂它的定义语法、调用机制、以及背后那些编译器默默为你做的“魔法”。无论你是刚接触泛型编程的新手还是想重新梳理基础的老鸟这篇文章都会让你对函数模板有一个通透的理解。2. 核心概念拆解泛型编程的基石在深入函数模板之前我们必须先建立几个关键概念。这能帮你理解模板设计的初衷而不仅仅是死记语法。2.1 什么是“模板”你可以把模板想象成一个蓝图或者模具。这个蓝图本身不是一个具体的函数或类它描述了一个函数或类的结构和算法但其中某些部分主要是类型是“留白”的。在C中这些“留白”用模板参数来标识。当你使用这个蓝图并为其“留白”处填入具体的类型比如int,std::string或值比如5,true时编译器就会根据这个蓝图和具体的填充物在编译期“浇筑”出一个完全符合你需求的、实实在在的函数或类。这个过程叫做模板实例化。2.2 泛型编程 vs. 面向对象编程这是两个不同的抽象维度常常被混淆。面向对象编程OOP关注的是数据的抽象。它通过继承和多态让不同类型的对象如Circle,Square能够通过统一的接口如Shape::draw()被操作。抽象发生在运行时。泛型编程GP关注的是算法的抽象。它让同一个算法如std::sort能够操作不同类型的数据如int,std::vectorMyClass而无需为每种类型重写算法。抽象发生在编译时。模板是C实现泛型编程的核心工具。标准模板库STL就是泛型编程最成功的典范其中的容器vector,map和算法sort,find都是模板。2.3 函数模板的核心价值消除代码冗余这是最直接的好处。写一次模板适用于多种类型。提升类型安全相比于使用void*来实现通用函数模板在编译期进行类型检查能及早发现类型不匹配的错误。带来更好的性能由于模板实例化是在编译期生成针对特定类型的代码因此没有运行时类型判断或转换的开销生成的代码与手写针对该类型的代码效率几乎一致。增强代码可维护性核心逻辑只存在于模板定义中一处。修改逻辑只需改一处所有实例化的版本都会自动更新。3. 函数模板的定义语法与语义深究知道了“为什么”我们来看“怎么做”。定义一个函数模板其语法核心是template关键字。3.1 基础定义语法template typename T T max(T a, T b) { return (a b) ? a : b; }我们来逐词解析template声明这是一个模板。typename T模板参数列表。typename是一个关键字用来声明一个类型模板参数名字叫T。你可以把T理解为一个占位符代表某种未知的类型。T这个名字是约定俗成的你可以用任何有效的标识符比如Type,Elem甚至MyTemplateType但简单场景下T最常用。T max(T a, T b)函数声明。这里的T就是上面声明的模板参数。它表示这个函数返回一个T类型的值接受两个T类型的参数。注意typename也可以用class关键字替代即template class T。在声明类型参数时两者完全等价。但typename语义更清晰“某种类型”而class可能让人误以为只能是类类型。现代C更推荐使用typename。3.2 多模板参数与默认参数模板参数可以不止一个也可以有默认值。// 多个类型参数 template typename T1, typename T2 auto add(T1 a, T2 b) - decltype(a b) { // 使用返回类型后置和decltype自动推导返回类型 return a b; } // 带有默认模板参数C11起 template typename T int // 默认T为int void printValue(T value) { std::cout value std::endl; } // 调用 printValue(42); // T被推导为int // 调用 printValuedouble(3.14); // 显式指定T为double3.3 非类型模板参数模板参数不一定非得是类型也可以是整型、枚举、指针或引用等值。这就是“非类型模板参数”。它在定义时必须是一个编译期常量。// 定义一个固定大小的数组包装类简化版 template typename T, std::size_t N // T是类型参数N是非类型参数大小 class FixedArray { public: T operator[](std::size_t index) { return data_[index]; } const T operator[](std::size_t index) const { return data_[index]; } std::size_t size() const { return N; } private: T data_[N]; // 数组大小在编译期就确定了 }; // 使用 FixedArrayint, 10 arr1; // 一个大小为10的int数组 FixedArraydouble, 100 arr2; // 一个大小为100的double数组 // 错误示例大小必须是编译期常量 // int size 10; // FixedArrayint, size arr3; // 编译错误size不是编译期常量 constexpr int size 10; // 正确size是编译期常量 FixedArrayint, size arr3; // 正确非类型模板参数的典型应用场景定义编译期确定的数组大小如上例。指定算法或策略的编译期常量如std::arrayT, N。实现模板元编程中的数值计算。实操心得非类型模板参数是编译期多态的重要手段。它允许你将一些“选项”从运行时转移到编译期编译器可以基于这些已知的常量进行优化比如循环展开从而提升运行时性能。但这也意味着不同的参数值会导致不同的类型可能增加代码体积模板膨胀。4. 函数模板的调用实例化与参数推导定义好模板只是第一步真正让它工作起来的是“调用”。调用模板函数时编译器会执行一系列复杂的操作。4.1 模板参数推导大多数时候你不需要显式指定模板参数。编译器会根据你传入的函数实参自动推导出模板参数T应该是什么类型。这个过程叫做模板实参推导。int i 1, j 2; auto m1 max(i, j); // 编译器推导i和j是int T int 实例化 maxint double x 3.14, y 2.71; auto m2 max(x, y); // 编译器推导T double 实例化 maxdouble std::string s1 hello, s2 world; // 假设std::string重载了 // auto m3 max(s1, s2); // 编译器推导T std::string 实例化 maxstd::string推导规则的核心编译器独立地推导每个模板参数。对于函数max(T a, T b)两个参数必须推导出相同的类型T否则推导失败。int a 1; double b 2.0; // auto m max(a, b); // 编译错误 // 对第一个实参a推导出 T int // 对第二个实参b推导出 T double // 矛盾推导失败。4.2 显式指定模板实参当自动推导失败或不符合你预期时你可以像调用普通函数一样在函数名后使用尖括号来显式指定模板参数。// 解决上面类型不匹配的问题 auto m maxdouble(a, b); // 显式告诉编译器T double // 编译器会将 int 类型的 a 隐式转换为 double然后调用 maxdouble显式指定的常见场景推导歧义如上例。模板参数不出现在函数参数列表中template typename T T getDefaultValue() { return T{}; // 返回T类型的默认值 } // auto val getDefaultValue(); // 错误编译器无法从空参数列表推导T auto val getDefaultValueint(); // 必须显式指定希望使用特定的转换你希望函数以某种特定的类型被实例化即使参数可以隐式转换到其他类型。4.3 模板实例化的过程当你调用max(i, j)且推导出T int时编译器在背后做了这些事推导确定模板参数T为int。实例化将模板定义template typename T T max(T a, T b)...中的每一个T替换为int生成一个实实在在的函数实体可以理解为int max(int a, int b) { // 编译器生成的代码 return (a b) ? a : b; }编译像编译普通函数一样编译这个新生成的maxint函数。链接如果其他编译单元也实例化了相同的maxint链接器会确保只有一个实体存在。这个过程对程序员是透明的但理解它对于调试模板相关的编译错误至关重要。注意事项模板的实例化是“按需”的。也就是说如果你从未用double调用过max那么maxdouble这个函数就永远不会被生成。这也意味着模板的定义而不仅仅是声明通常必须放在头文件中因为编译器需要在每个用到它的编译单元里看到完整的定义才能进行实例化。这是模板与普通函数在代码组织上的一个关键区别。5. 深入模板重载、特化与SFINAE掌握了基础定义和调用我们可以看看更高级的用法这些是写出健壮、灵活模板代码的关键。5.1 函数模板的重载函数模板可以和普通函数以及其他函数模板重载。编译器在选择调用哪个函数时遵循一个非常复杂的重载决议规则但核心优先级通常如下完全匹配的普通函数。完全匹配的模板函数。通过隐式转换能匹配的普通函数。通过隐式转换能匹配的模板函数。// 普通函数 void log(int x) { std::cout Log int: x std::endl; } // 函数模板 template typename T void log(T x) { std::cout Log template: x std::endl; } // 另一个函数模板更特化 template typename T void log(T* x) { std::cout Log pointer: *x std::endl; } int main() { int a 5; int* p a; log(a); // 调用普通函数 log(int)优先级最高 log(p); // 调用 log(T*)比 logT(T) 更特化 log(3.14); // 调用 logdouble(double)普通函数log(int)需要转换模板更匹配 log(hello); // 调用 logconst char*(const char*) }5.2 模板特化为特定类型定制行为有时候对于某些特定的类型通用的模板算法可能效率不高甚至无法工作。这时你可以为这些类型提供特化版本。// 通用模板 template typename T bool isEqual(T a, T b) { return a b; } // 为const char* (C风格字符串)提供特化版本 template bool isEqualconst char*(const char* a, const char* b) { return strcmp(a, b) 0; } // 使用 int main() { std::cout isEqual(1, 1) std::endl; // 调用通用版本 const char* s1 hello; const char* s2 hello; std::cout isEqual(s1, s2) std::endl; // 调用特化版本进行字符串比较 // 注意直接写 isEqual(hello, hello) 可能会推导出 const char[6] 类型行为可能不同这是另一个话题。 }重要提示函数模板的特化不如类模板特化常用并且可能带来一些令人惊讶的重载决议结果。更现代、更推荐的做法是使用函数重载如上节的log例子或者C11的constexpr ifC17来在编译期选择不同代码路径这通常更清晰、更可控。5.3 SFINAE替换失败并非错误这是一个高级但极其重要的概念。SFINAE 是“Substitution Failure Is Not An Error”的缩写。它描述了模板推导和重载决议中的一个基本原则当编译器尝试用实参推导模板参数时如果推导或替换导致了一个非法的类型或表达式编译器不会立即报错而是简单地将这个模板从重载集中丢弃然后继续尝试其他可行的重载。#include iostream #include type_traits // 这个模板只对具有 size_type 嵌套类型的类型有效 template typename T auto getSize(const T container) - decltype(container.size(), typename T::size_type()) { std::cout Has size() member. std::endl; return container.size(); } // 重载版本处理没有size()的类C数组 template typename T, std::size_t N std::size_t getSize(T (array)[N]) { std::cout Is array. std::endl; return N; } // 一个简单的“假”容器没有size_type struct DummyContainer { int data[10]; // 没有 size() 成员函数 }; int main() { std::vectorint vec{1,2,3}; int arr[5] {1,2,3,4,5}; DummyContainer dummy; std::cout getSize(vec) std::endl; // 调用第一个模板推导成功 std::cout getSize(arr) std::endl; // 调用第二个模板数组版本 // std::cout getSize(dummy) std::endl; // 编译错误 // 第一个模板decltype中的 container.size() 替换失败根据SFINAE此模板被丢弃。 // 第二个模板参数 T ()[N] 无法匹配 DummyContainer 类型。 // 没有可行的重载编译错误。 }SFINAE是很多现代C模板技巧如std::enable_if的基础用于在编译期根据类型特性启用或禁用某些模板。6. 实战编写健壮的通用函数模板理论说再多不如动手写一个。我们来设计一个clamp函数模板它接受一个值、一个最小值和一个最大值如果值小于最小值则返回最小值大于最大值则返回最大值否则返回原值。这是一个非常实用的通用工具函数。6.1 第一版基础实现template typename T const T clamp(const T value, const T low, const T high) { // 注意这里假设 low high生产代码需要断言或处理 if (value low) return low; if (value high) return high; return value; }这个版本很简单但有几个潜在问题它要求T类型支持和操作符。它返回const T避免了不必要的拷贝但要求传入的low和high生命周期足够长。它没有检查low high。6.2 第二版增强安全性与概念约束C20我们可以利用C20的concepts来明确约束模板参数并加入断言。#include concepts // C20 #include cassert // 要求类型T是可全序比较的 template std::totally_ordered T const T clamp(const T value, const T low, const T high) { assert(low high low must be less than or equal to high); if (value low) return low; if (value high) return high; return value; } // 如果没有C20可以用SFINAE或static_assert模拟 template typename T const T clamp_old(const T value, const T low, const T high) { static_assert(std::is_arithmetic_vT || /* 其他自定义类型检查 */, T must be a comparable type); assert(low high); // ... 同上 }6.3 第三版处理混合类型进阶有时我们可能想比较一个int和一个double。基础版本要求三个参数类型相同。我们可以通过引入额外的模板参数和返回类型推导来实现更灵活的版本。template typename T, typename U, typename V // 使用 decltype 和 std::common_type_t 来推导最合适的返回类型 auto clamp_mixed(const T value, const U low, const V high) - std::common_type_tT, U, V // 返回三种类型都能无损转换到的公共类型 { using CommonType std::common_type_tT, U, V; // 将参数转换为公共类型再比较避免精度丢失和警告 CommonType c_value value; CommonType c_low low; CommonType c_high high; assert(c_low c_high); if (c_value c_low) return c_low; if (c_value c_high) return c_high; return c_value; }这个版本更通用但逻辑也稍复杂。std::common_type_t是标准库工具用于确定一组类型的公共类型。7. 常见陷阱与最佳实践模板功能强大但也容易踩坑。下面是我总结的一些常见问题和应对策略。7.1 陷阱一代码膨胀每一次用不同的类型实例化模板编译器都会生成一份该类型的代码。如果模板函数体很大且被用于很多不同类型最终的可执行文件可能会显著增大。对策将模板代码中与类型无关的部分抽取到非模板的辅助函数中。确保模板函数体尽量精简。7.2 陷阱二编译错误信息晦涩难懂模板相关的编译错误尤其是涉及深层嵌套或SFINAE时错误信息可能非常冗长和难以理解。对策使用静态断言static_assert在模板开头对类型进行约束给出清晰的错误信息。template typename T void process(T val) { static_assert(std::is_integral_vT, process() requires integral types.); // ... }使用C20 Concepts这是解决此问题的最佳语言特性能提供最清晰的约束和错误信息。从错误信息最后一行往前看通常最后一行指出了最根本的问题。7.3 陷阱三分离编译问题如前所述模板定义通常必须放在头文件里。如果你将模板声明放在.h定义放在.cpp然后在另一个.cpp文件中使用它会导致链接错误因为编译器在用到它的地方看不到定义无法实例化。对策始终将模板的完整定义包括函数体放在头文件中。对于大型项目可以将模板的实现细节放在一个后缀为.ipp或.tpp的文件中然后在头文件末尾#include它。7.4 陷阱四对ADL参数依赖查找的影响ADLKoenig查找规则在模板中尤为重要。当调用一个函数时编译器不仅会在当前作用域查找还会在函数参数类型所属的命名空间中查找。namespace MyLib { class MyClass {}; void swap(MyClass, MyClass); // 自定义swap } template typename T void myAlgorithm(T a, T b) { using std::swap; // 引入std::swap到当前作用域 swap(a, b); // 正确通过ADL会找到 MyLib::swap 如果T是MyClass }在模板中调用swap这类函数时使用using std::swap;然后调用无限制的swap(a, b)是一种最佳实践它允许ADL找到用户自定义的最佳swap同时回退到std::swap。7.5 最佳实践总结优先使用typename而非class声明类型参数。为模板参数起有意义的名字尤其是多个参数时如typename Key, typename Value。在头文件中定义模板。使用const引用传递大型或不可复制对象到模板函数避免不必要的拷贝。利用auto和返回类型后置来处理复杂的返回类型推导。用static_assert或 Concepts 约束模板参数尽早给出友好错误。警惕非类型模板参数带来的代码膨胀。理解并善用SFINAE和重载决议来编写更灵活的模板。对于需要特化的情况优先考虑函数重载而非函数模板特化。函数模板是C泛型编程的入口理解它就拿到了打开STL和现代C元编程大门的钥匙。它要求你从“编写特定类型的代码”转向“编写与类型无关的算法”这种思维转变是成为高级C程序员的必经之路。刚开始可能会觉得编译错误很吓人但只要你理解了模板实例化和SFINAE的基本原理多写多试这些“纸老虎”都会变成你强大的工具。

相关新闻

2026无头电商架构深度拆解:核心原理、落地优劣与适配场景全攻略

2026无头电商架构深度拆解:核心原理、落地优劣与适配场景全攻略

无头电商并非复杂的新兴技术概念,核心是电商前后端彻底解耦的架构模式,依托API接口实现前后端独立协作,是当下电商架构升级的主流方向。综合实操落地经验来看,无头电商长期扩展性、多端适配能力远超传统一体式电商架构&#xff0c…

2026/8/23 6:45:33 阅读更多 →
嵌入式开发必备:逻辑分析仪核心原理与实战调试指南

嵌入式开发必备:逻辑分析仪核心原理与实战调试指南

1. 项目概述:为什么嵌入式工程师离不开逻辑分析仪?如果你正在学习嵌入式开发,或者已经是一名嵌入式工程师,那么“逻辑分析仪”这个名字对你来说一定不陌生。但很多时候,它就像一个熟悉的陌生人——你知道它很重要&…

2026/8/23 6:44:33 阅读更多 →
零:NorpAgent 元框架开发系列教程引言

零:NorpAgent 元框架开发系列教程引言

0.1 它是什么? 在开始之前,你需要明白: NorpAgent 远远不止是一个智能体框架。 智能体框架通常会替你决定“Agent 应该长什么样”——它规定模型怎么调用、工具怎么注册、记忆怎么存储、循环怎么运转。你用它的“配方”来构建应用。 而 Nor…

2026/8/23 6:44:33 阅读更多 →

最新新闻

Kimi    LeetCode LCP 35. 电动车游城市 Java实现

Kimi LeetCode LCP 35. 电动车游城市 Java实现

根据已收集的信息,我来为你提供 LCP 35. 电动车游城市 的 Java 实现。题目分析这道题是分层图最短路问题。核心思路是将状态定义为 (城市, 电量) 二元组,然后使用 Dijkstra 算法求最短时间。- 状态设计:dist[i][j] 表示到达第 i 个城市时剩余…

2026/8/23 7:36:48 阅读更多 →
Jmeter安装插件-Response Times Over Time-查看并发结果

Jmeter安装插件-Response Times Over Time-查看并发结果

install插件网址:JMeter Plugins Manager :: JMeter-Plugins.org 安装后重启:

2026/8/23 7:36:48 阅读更多 →
机器学习在母婴健康风险评估中的应用:以孕妇吸烟与胎儿健康为例

机器学习在母婴健康风险评估中的应用:以孕妇吸烟与胎儿健康为例

1. 项目概述:当数据科学遇见母婴健康母婴健康一直是公共卫生和临床医学关注的核心领域,而孕妇吸烟对胎儿发育的潜在危害,更是被广泛讨论却难以精确量化的复杂问题。传统的流行病学研究依赖于问卷调查和回顾性分析,往往受限于样本量…

2026/8/23 7:36:48 阅读更多 →
大模型预训练数据要求 + 完整数据清洗流程

大模型预训练数据要求 + 完整数据清洗流程

一、预训练数据核心要求 1. 基础维度要求表 维度详细标准说明数据规模千亿~万亿 Token 级别通用大模型依赖海量文本,数据量不足会出现知识匮乏、逻辑差领域多样性通用:网页、书籍、论文、对话、代码、百科、新闻单一领域数据会造成模型偏科…

2026/8/23 7:36:48 阅读更多 →
Java面向对象特性与面试核心知识点解析

Java面向对象特性与面试核心知识点解析

1. Java基础面试题深度解析:从理论到实战作为一名Java开发者,面试中经常会被问到基础概念。很多人对这些问题的理解停留在表面,今天我将结合自己多年的开发经验,带大家深入理解这些核心知识点。1.1 面向对象三大特性解析1.1.1 封装…

2026/8/23 7:36:48 阅读更多 →
东华大学考研机试:KMP优化与动态规划实战

东华大学考研机试:KMP优化与动态规划实战

1. 项目背景与核心价值作为一名计算机专业考研党,我深知东华大学复试机试环节的重要性。去年备考期间,我坚持每天刷3道OJ题目并详细复盘,最终在复试中取得了优异成绩。这套"每日3题打卡深度复盘"的方法论,不仅帮助我系统…

2026/8/23 7:35:48 阅读更多 →

日新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/23 0:00:50 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/23 0:00:50 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/23 0:00:50 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/23 0:00:50 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/23 0:00:50 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/23 0:00:50 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/22 18:08:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/22 7:31:03 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →