【C++】C++11 核心特性深度解析(二) - 实践

news/2026/1/24 19:47:46/文章来源:https://www.cnblogs.com/ljbguanli/p/19527382

【C++】C++11 核心特性深度解析(二) - 实践


一、类型分类与值类别


1.1 左值 / 右值 / 将亡值——三张“身份证”

把表达式按“身份 + 可移动性”拆成五类,日常编码只需掌握核心三兄弟:

类别英文典型例子核心特征
左值lvalueobj*ptra[i]++x有持久身份,可取地址,不能绑定到 T&&
将亡值xvaluestd::move(obj), 返回 T&& 的函数调用有身份但即将被“掏空”,是“可移动的左值”
纯右值prvalue42T()a+b没有身份,占临时对象,生命周期到完整表达式结束

记忆公式:
“左值能取地址,将亡值能移动,纯右值是临时。”

 1.2 泛左值与纯右值——给五类表达式“归大类”

标准把五类再归成两大族:

  • glvalue(generalized lvalue) = 左值 + 将亡值
    共同点:有身份(identity),代表内存位置

  • prvalue(pure rvalue) = 纯右值
    代表“初始化器”或“计算结果”,不固定内存位置

示意图:

glvalue  ┬─ lvalue
         └─ xvalue (将亡值)
prvalue

引入 glvalue 的目的:让“可移动对象”同时保有身份,为移动语义铺路。

 1.3 值类别判断口诀

面对任意表达式,按顺序问两句:

  1. 能不能取地址 &expr
    → 能 → 左值
    → 不能 → 继续 2.

  2. 有没有名字(变量名)且类型是对象引用 / 返回 T&&
    → 有 → 将亡值
    → 无 → 纯右值

int  a = 1;
int& f();
int&& g();
&a;        // 1. 能取地址 → 左值
&(a + 1);  // 1. 不能取地址 → 2. 无名字 → 纯右值
&f();      // 1. 能取地址 → 左值
&g();      // 1. 不能取地址 → 2. 返回 T&& 且有名字 → 将亡值

二、引用折叠规则

 2.1 折叠表与推导示例

  • C++中不能直接定义引用的引用如 int& && r = i; ,这样写会直接报错,通过模板或 typedef中的类型操作可以构成引用的引用。
  • 通过模板或 typedef 中的类型操作可以构成引用的引用时,这时C++11给出了⼀个引用折叠的规则:右值引用的右值引用折叠成右值引用,所有其他组合均折叠成左值引用。
  • 下面的程序中很好的展示了模板和typedef时构成引用的引用时的引用折叠规则,大家需要⼀个⼀个仔细理解⼀下。
  • 像f2这样的函数模板中,T&& x参数看起来是右值引用参数,但是由于引用折叠的规则,他传递左值时就是左值引用,传递右值时就是右值引用,有些地方也把这种函数模板的参数叫做万能引用。
  • Function(T&& t)函数模板程序中,假设实参是int右值,模板参数T的推导int,实参是int左值,模板参数T的推导int&,再结合引用折叠规则,就实现了实参是左值,实例化出左值引用版本形参的Function,实参是右值,实例化出右值引用版本形参的Function。
// 由于引⽤折叠限定,f1实例化以后总是⼀个左值引⽤
template
void f1(T& x)
{}
// 由于引⽤折叠限定,f2实例化后可以是左值引⽤,也可以是右值引⽤
template
void f2(T&& x)
{}
int main()
{typedef int& lref;typedef int&& rref;int n = 0;lref& r1 = n; // r1 的类型是 int&lref&& r2 = n; // r2 的类型是 int&rref& r3 = n; // r3 的类型是 int&rref&& r4 = 1; // r4 的类型是 int&&//总结 右值引用的右值引用 才为右值引用// 没有折叠->实例化为void f1(int& x)f1(n);//f1(0); // 报错// 折叠->实例化为void f1(int& x)f1(n);//f1(0); // 报错// 折叠->实例化为void f1(int& x)f1(n);//f1(0); // 报错// 折叠->实例化为void f1(const int& x)f1(n);f1(0);// 折叠->实例化为void f1(const int& x)f1(n);f1(0);// 没有折叠->实例化为void f2(int&& x)//f2(n); // 报错f2(0);// 折叠->实例化为void f2(int& x)f2(n);//f2(0); // 报错// 折叠->实例化为void f2(int&& x)//f2(n); // 报错f2(0);return 0;
}

 2.2 转发引用(Universal Reference)

Tip:转发引用也称为万能引用

只有同时满足两条才算转发引用:

  1. 函数模板 && 形参

  2. T 由推导而来(不能是已经确定的类型)

反例对比:

template
void bar(T&& t);          // ✅ 转发引用
template
class X {void baz(T&& t);      // ❌ T 在类实例化时固定,是普通右值引用
};
void f(const int&& t);    // ❌ 无模板推导

 转发(万能)引用代码示例

//万能引用 传左值 推导+引用折叠->>左值引用 传右值 推导+引用折叠 ->> 右值引用
template
void Function(T&& t)
{int a = 0;T x = a;//x++;cout << &a << endl;cout << &x << endl << endl;
}
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// b是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int& t)// 所以Function内部会编译报错,x不能++Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)// 所以Function内部会编译报错,x不能++Function(std::move(b)); // const 右值return 0;
}

2.3 完美转发 std::forward 实现剖析

  • Function(T&& t)函数模板程序中,传左值实例化以后是左值引的Function函数,传右值实例化以后是右值引用的Function函数。
  • 但是结合我们前面讲的,变量表达式都是左值属性,也就意味着⼀个右值被右值引用绑定后,右值引用变量表达式的属性是左值,也就是说Function函数中t的属性是左值,那么我们把t传递给下⼀层函数Fun,那么匹配的都是左值引用版本的Fun函数。这⾥我们想要保持t对象的属性,就需要使用完美转发实现。
  • 完美转发forward本质是⼀个函数模板,他主要还是通过引用折叠的方式实现,下面实例中传递给Function的实参是右值,T被推导为int,没有折叠,forward内部t被强转为右值引用返回;传递给Function的实参是左值,T被推导为int&,引用折叠为左值引用,forward内部t被强转为左值引用返回。
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; }
template
void Function(T&& t)
{/*右值引⽤变量表达式的属性是左值,也就是说Function函数中t的属性是左值,那么我们把t传递给下⼀层函数Fun,那么匹配的都是左值引⽤版本的Fun函数。这⾥我们想要保持t对象的属性,就需要使⽤完美转发实现。*///Fun(t); // 本身属性左值 这样传下去 调用的全是左值引用Fun(forward(t));
}
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// b是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int& t)Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&& t)Function(std::move(b)); // const 右值return 0;
}

三、可变参数模板


3.1 语法与包(Parameter Pack)基础

C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函数参数。

  • template <class… Args> void Func(Args… args) {} 传值参数包

  • template <class… Args> void Func(Args&… args) {} 左值引用参数包

  • template <class… Args> void Func(Args&&… args) {} 万能引用参数包

  • 我们用省略号来指出⼀个模板参数或函数参数的表示⼀个包,在模板参数列表中,class…或typename…指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟…指出接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示,跟前面普通模板⼀样,每个参数实例化时遵循引用折叠规则。

  • 可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。

  • 这里我们可以使用sizeof…运算符去计算参数包中参数的个数。

template 
void Print(Args&&... args)
{cout << sizeof...(args) << endl;
}
int main()
{double x = 2.2;Print(); // 包⾥有0个参数Print(1); // 包⾥有1个参数Print(1, string("xxxxx")); // 包⾥有2个参数Print(1.1, string("xxxxx"), x); // 包⾥有3个参数return 0;
}
// 原理1:编译本质这⾥会结合引⽤折叠规则实例化出以下四个函数
void Print();
void Print(int&& arg1);
void Print(int&& arg1, string&& arg2);
void Print(double&& arg1, string&& arg2, double& arg3);
// 原理2:更本质去看没有可变参数模板,我们实现出这样的多个函数模板才能⽀持
// 这⾥的功能,有了可变参数模板,我们进⼀步被解放,他是类型泛化基础
// 上叠加数量变化,让我们泛型编程更灵活。
void Print();
template 
void Print(T1&& arg1);
template 
void Print(T1&& arg1, T2&& arg2);
template 
void Print(T1&& arg1, T2&& arg2, T3&& arg3);


3.2 包展开(Pack Expansion)模式

包扩展就是把参数包拆解的过程。
注意包扩展的过程是在编译时,不是运行时。

  • 对于⼀个参数包,我们除了能计算他的参数个数,我们能做的唯一的事情就是扩展它,当扩展⼀个包时,我们还要提供用于每个扩展元素的模式,扩展⼀个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式的右边放⼀个省略号(…)来触发扩展操作。
  • C++还支持更复杂的包扩展,直接将参数包依次展开依次作为实参给一个函数去处理。
 //可变模板参数//参数类型可变//参数个数可变//打印参数包内容
template 
void Print(Args... args)
{// 可变参数模板编译时解析// 下⾯是运⾏获取和解析,所以不⽀持这样⽤cout << sizeof...(args) << endl;for (size_t i = 0; i < sizeof...(args); i++){cout << args[i] << " ";}
}void ShowList(){// 编译器时递归的终⽌条件,参数包是0个时,直接匹配这个函数cout << endl;}template void ShowList(T x, Args... args){cout << x << " ";// args是N个参数的参数包// 调⽤ShowList,参数包的第⼀个传给x,剩下N-1传给第⼆个参数包ShowList(args...);}// 编译时递归推导解析参数template void Print(Args... args){ShowList(args...);}/*int main(){Print();Print(1);Print(1, string("xxxxx"));Print(1, string("xxxxx"), 2.2);return 0;}*///template //void ShowList(T x, Args... args)//{// cout << x << " ";// Print(args...);//}// Print(1, string("xxxxx"), 2.2);调⽤时// 本质编译器将可变参数模板通过模式的包扩展,编译器推导的以下三个重载函数函数//void ShowList(double x)//{// cout << x << " ";// ShowList();
//}
//
//void ShowList(string x, double z)
//{
// cout << x << " ";
// ShowList(z);
//}
//
//void ShowList(int x, string y, double z)
//{
// cout << x << " ";
// ShowList(y, z);
//}
//void Print(int x, string y, double z)
//{
// ShowList(x, y, z);
//}template const T& GetArg(const T& x){cout << x << " ";return x;}template void Arguments(Args... args){}template void Print(Args... args){// 让 GetArg 返回有值的对象(哪怕只用来占位),这样才能组成参数包给ArgumentsArguments(GetArg(args)...);}// 本质可以理解为编译器编译时,包的扩展模式// 将上⾯的函数模板扩展实例化为下⾯的函数// 是不是很抽象,C++11以后,只能说委员会的⼤佬设计语法思维跳跃得太厉害//void Print(int x, string y, double z)//{// Arguments(GetArg(x), GetArg(y), GetArg(z));//}int main(){Print(1, string("xxxxx"), 2.2);return 0;}

包扩展还有另一种方式。

3.3empalce系列接口

  • template <class… Args> void emplace_back (Args&&… args);
  • template <class… Args> iterator emplace (const_iterator position,Args&&… args);
  • C++11以后STL容器新增了empalce系列的接口,empalce系列的接口均为模板可变参数,功能上兼容push和insert系列,但是empalce还支持新玩法,假设容器为container,empalce还支持直接插入构造T对象的参数,这样有些场景会更高效⼀些,可以直接在容器空间上构造T对象。
  • emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列
  • 第二个程序中我们模拟实现了list的emplace和emplace_back接口,这里把参数包不断往下传递,最终在结点的构造中直接去匹配容器存储的数据类型T的构造,所以达到了前面说的empalce支持直接插入构造T对象的参数,这样有些场景会更高效⼀些,可以直接在容器空间上构造T对象。
  • 传递参数包过程中,如果是 Args&&… args 的参数包,要用完美转发参数包,方式如下std::forward(args)… ,否则编译时包扩展后右值引用变量表达式就变成了左值。
std::listlt1;
//效率用法都是一样的
lt1.push_back(1);
lt1.emplace_back(2);
//效率用法都是一样的
std::listlt2;
xc::string s1("1111111111");
lt2.push_back(s1);
lt2.emplace_back(s1);
cout << "***************************************************************" << endl;
//效率用法都是一样的
xc::string s2("22222222222");
lt2.push_back(move(s2));
xc::string s3("2222222222");
lt2.emplace_back(move(s3));
cout << "***************************************************************" << endl;
// 优化区别
lt2.push_back("111111111111111111111111"); //构造+移动构造 析构匿名 类模板 类型 确定为 string&&
lt2.emplace_back("111111111111111111111111"); // 直接构造 函数模板 类型确定为  const char *
//push_back 要先造一个临时 string再搬进容器;
//emplace_back 把参数直接丢进容器内存,让容器里那个 string 就地用 const char* 构造,省掉临时对象及其后续移动 / 析构。
cout << "***************************************************************" << endl;

这里出现的优化区别是参数优先匹配级导致的,“谁更直接、谁更特殊、谁就少转换” —— 编译器按标准重载决议三部曲挑最匹配的那个。

回到 list 的尾插
实参:const char* 字面量 "111..."

候选函数:

// A. 普通成员函数(已实例化)
void push_back(const std::string&);   // 需用户定义转换 ③
void push_back(std::string&&);        // 需用户定义转换 ③
// B. 成员函数模板(需推导)
template
void emplace_back(Args&&... args);    // 精确匹配 ①

路径对比

函数转换链重载等级是否特殊
push_backconst char* → std::string(临时)③ 用户定义转换
emplace_backArgs = const char* 直接绑定① 精确匹配是(模板)

精确匹配优于用户定义转换,因此
emplace_back 胜出,不会产生 std::string 临时对象。


多参数的pair也是一样的道理

std::list> lt3;
//传左值效率用法一样
pairkv1("xxxxxx", 1);
lt3.push_back(kv1);
lt3.emplace_back(kv1);
cout << "***************************************************************" << endl;
//传右值效率用法一样
pairkv2("yyyyyyy", 2);
lt3.push_back(move(kv2));
pairkv3("yyyyyyy", 2);
lt3.emplace_back(move(kv3));
cout << "***************************************************************" << endl;
// 直接传参数构造 更高效率
lt3.push_back({ "xxxxxxxxxx",1 });
//lt3.emplace_back({ "xxxxxxxxxx",1 }); //报错 花括号 识别为 initializer_list 不支持不同参数类型
lt3.emplace_back("xxxxxxxxxx", 1); //万能引用参数包直接识别构造

//lt3.push_back("xxxxxxxxxx", 1); //报错 push_back 单参数函数
lt3.push_back({ "xxxxxxxxxx",1 });
//lt3.emplace_back({ "xxxxxxxxxx",1 }); //报错 花括号 识别为 initializer_list 不支持不同参数类型
lt3.emplace_back("xxxxxxxxxx", 1); //万能引用参数包直接识别构造

lt3.push_back("xxxxxxxxxx", 1);
报错位置:语法检查阶段

  • push_back 只有 单参数 重载

  • 你塞了 两个实参直接语法错误,连重载决议都进不去

报错:no matching function for call to 'push_back'


lt3.push_back({ "xxxxxxxxxx", 1 });
能过(C++11/14/17 默认模式)

  • 花括号列表 {} 被当成 单个子表达式

  • 编译器尝试 复制列表初始化 把该子表达式变成 value_type
    (对 map 就是 std::pair<const std::string, int>

  • pair非 explicit 模板构造函数

template
pair(U1&& a, U2&& b);

于是推导成功,生成一个临时 pair,再移动进链表

结果:隐式转换 + 一次移动构造
⚠️ C++20 若开启严格列表初始化检查会失败,但主流编译器默认仍放过


lt3.emplace_back({ "xxxxxxxxxx", 1 });
报错:花括号跨模板推导失败

  • emplace_back函数模板

template
void emplace_back(Args&&... args);
  • 整个花括号列表当成一个实参 传进去 → 编译器需要推导 Args
    但花括号列表 没有类型,也无法推导出 Args = {const char*, int}
    模板推导失败

报错:no matching function for call to 'emplace_back'
根源:花括号列表不能跨函数模板边界做类型推导(标准硬规则)

lt3.emplace_back("xxxxxxxxxx", 1);
最优:零临时、零歧义

  • 两个实参 本身有类型 (const char*, int)

  • 模板推导得到 Args = [const char*, int]

  • 内部直接 placement new:

new (node) std::pair("xxxxxxxxxx", 1);
  • 无临时 pair无移动构造无花括号歧义


想要增加emplace系列直接这样写就可以,注意完美转发保持属性。

void emplace_back(Args&&... args)
{insert(end(), std::forward(args)...);
}
template 
iterator insert(iterator pos, Args&&... args)
{Node* cur = pos._node;Node* newnode = new Node(std::forward(args)...);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}
template
list_node(X&&... data):data(forward(data)...), next(nullptr), prev(nullptr)
{}

原理就是编译器根据可变模版参数生成对应参数的函数。

同时说明我们拿到参数包一定要包扩展吗?
不是,这里我们直接把参数包往下传就可以了。
当传到data这里时直接匹配对应的对应构造即可。需要包扩展。只有当用到参数包时再包扩展。

后言

这就是C++11(二)。大家自己好好消化!感谢各位的耐心垂阅!咱们下期见!拜拜~

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1210953.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    AI原生应用领域模型量化的安全考量

    AI原生应用领域模型量化的安全考量 关键词:模型量化、AI原生应用、安全风险、对抗攻击、隐私保护 摘要:随着AI原生应用(完全基于AI技术构建的应用,如智能驾驶助手、医疗诊断系统)的普及,模型量化作为提升部署效率的核心技术被广泛使用。但量化过程可能引入精度损失、对抗…

    一天一个开源项目(第1篇):everything-claude-code - 最全的 Claude Code 配置集合

    引言 “好的工具配置能让 AI 助手从’能用’变成’好用’&#xff0c;从’助手’变成’伙伴’。” 这是"一天一个开源项目"系列的第1篇文章。今天带你了解的项目是 everything-claude-code&#xff08;GitHub&#xff09;。 如果你正在使用 Claude Code&#xff08;…

    搭建终身学习系统时,AI应用架构师容易犯哪些错?(避坑指南)

    AI应用架构师搭建终身学习系统的10个常见坑与避坑指南 副标题&#xff1a;从数据管道到模型部署的实践教训 摘要/引言 在AI从“静态工具”转向“动态系统”的今天&#xff0c;终身学习&#xff08;Lifelong Learning&#xff09; 已成为企业保持AI竞争力的核心能力——它让模型…

    学霸同款2026 AI论文工具TOP9:专科生毕业论文写作全测评

    学霸同款2026 AI论文工具TOP9&#xff1a;专科生毕业论文写作全测评 2026年AI论文工具测评&#xff1a;为何值得一看&#xff1f; 随着人工智能技术的不断进步&#xff0c;越来越多的专科生开始借助AI工具提升论文写作效率。然而&#xff0c;面对市场上琳琅满目的产品&#x…

    前端萌新别慌!30分钟搞懂CSS阴影:text-shadow和box-shadow实

    前端萌新别慌&#xff01;30分钟搞懂CSS阴影&#xff1a;text-shadow和box-shadow实 前端萌新别慌&#xff01;30分钟搞懂CSS阴影&#xff1a;text-shadow和box-shadow实战指南先别急着写代码&#xff0c;咱先吐槽五分钟先整点能跑的&#xff0c;把士气提上来text-shadow&#…

    AI编程实战 : 使用 TRAE CN 将 MasterGo 设计稿转化为前端代码

    文章目录 什么是 MCP前置条件1. 账号权限2. 环境要求3. 设计稿准备 MasterGo AI Bridge 支持的能力操作步骤第一步: 安装/升级 TRAE CN IDE第二步: 获取 MasterGo 的 Personal Access Token第三步: 添加 MCP Server第四步: 创建自定义智能体&#xff08;可选&#xff09;第五步…

    实用指南:Linux Crontab命令详解:轻松设置周期性定时任务

    pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

    分析一下当前项目如果browser或者node包需要引用common包中的方法,如何设计项目架构

    当前项目架构分析:项目使用 pnpm workspace 管理 monorepo 有三个包:common(通用)、browser(浏览器专用)、node(Node.js专用) 每个包都是独立的 npm package,有自己的 package.json 当前三个包之间没有任何依…

    导师推荐8个AI论文写作软件,继续教育学生轻松搞定毕业论文!

    导师推荐8个AI论文写作软件&#xff0c;继续教育学生轻松搞定毕业论文&#xff01; AI 工具让论文写作不再难 在当前的学术环境中&#xff0c;继续教育学生面临着越来越高的论文写作要求。无论是本科、硕士还是博士阶段&#xff0c;撰写一篇高质量的毕业论文不仅是对知识的总…

    Firewalld 配置端口转发、SNAT、DNAT

    Firewalld通过富规则(rich rules)和直接规则(direct rules)支持端口转发和NAT。 下面详细说明各种配置方法: 端口转发(Port Forwarding) 方法1:使用富规则(推荐) 基础端口转发(本地转发) # 将外部访问 808…

    探索AI原生应用领域AI工作流的新趋势

    探索AI原生应用领域AI工作流的新趋势 引言:从“AI+应用”到“AI原生应用”的范式转移 2023年以来,AI原生应用(AI-Native Application)成为科技行业最热门的关键词之一。从Notion AI的智能笔记、GitHub Copilot X的全生命周期开发辅助,到Perplexity的实时知识问答,这些应…

    AI智能体完全指南:无需编程基础,四步打造专属AI助手

    本文详解AI智能体搭建方法&#xff0c;从入门到进阶&#xff0c;包括四步创建流程、个性化设置、知识库运用和指令迭代技巧。文章强调将AI视为协作者而非工具&#xff0c;根据场景选择适合的大模型&#xff0c;并提供多个国内免费平台推荐。无需编程基础&#xff0c;即可打造专…

    指针与数组:为什么数组名是特殊的指针?

    指针与数组&#xff1a;为什么数组名是特殊的指针&#xff1f; 在C编程中&#xff0c;指针与数组的关系是入门阶段的核心难点&#xff0c;也是高频考点。很多开发者会发现一个有趣的现象&#xff1a;数组名既能像普通变量一样通过下标访问元素&#xff0c;又能像指针一样进行地…

    指针进阶:二级指针与指针的指针的应用场景

    指针进阶&#xff1a;二级指针与指针的指针的应用场景 在C指针学习中&#xff0c;二级指针&#xff08;又称指针的指针&#xff09;是从基础指针迈向进阶的关键节点。前文我们了解到&#xff0c;一级指针存储的是普通变量的内存地址&#xff0c;而二级指针的核心是“存储一级指…

    AI人工智能-RAG方法-第十四周(小白)

    一、RAG到底是什么? RAG是 Retrieval Augmengted Generation(检索增强生成)的缩写,核心逻辑特别好理解——就像我们写作文时,先查资料再动笔,而不是凭脑子硬记硬写。 简单说:AI回答问题时,不会只靠自己“记住”的知识,而是从外部文档库(或搜索引擎)里检索出和问题相…

    AI人工智能-Agent相关介绍-第十四周(小白)

    一、Agent是什么 Agent翻译过来时“智能体”,你可以把它理解为一个“有自主能力的智能助手”——它不用人一步步指挥,能自己理解任务,规划步骤、使用工具、记住过往经历,甚至和其他Agent或人类协作,最终完成目标。 简单说:普通LLM是“你问我答”的工具,二Agent是“你交…

    AI人工智能-Function Call 与MCP-第十四周(小白)

    一、Function Call是什么 Function Call直译是“函数调用”,但用通俗的话讲,它就是 LLM 的 “工具箱使用能力”—— 就像我们遇到算不清的数学题会拿计算器,LLM 遇到自己搞不定的问题(比如查实时数据、复杂计算、翻译),会 “喊工具来帮忙”。 核心逻辑:LLM 当 “决策者…

    基于西门子PLC1214C的三原料自动称重配料搅拌系统程序修改探讨

    基于西门子PLC1214C三原料自动称重配料搅拌系统改程序仅用于学时探讨。 功能&#xff1a; 三个原料仓按照配比先称重&#xff0c;然后进入配料仓&#xff0c;配料仓有两个重量档位&#xff0c;可以手动选择&#xff0c;当原料在配料仓里满足档位要求&#xff0c;原料仓停止称重…

    AI原生应用中的多模态交互:从理论到实践

    AI原生应用中的多模态交互:从理论到实践 关键词 多模态交互、AI原生应用、跨模态对齐、多模态大模型、具身智能、用户意图理解、模态融合策略 摘要 本报告系统解析AI原生应用中多模态交互的核心技术体系,覆盖从理论基础到工程实践的全链路。通过第一性原理推导(信息论+认…

    os安装-winoffice在线激活命令

    1.管理员运行 power shell 2.输入如下命令(直接复制粘贴) irm https://get.activated.win | iex 3.根据提示操作即可 1 系统激活 2 office激活 ...亲测 office365激活有效 2024专业增强版