完整教程:C++_chapter15_C++重要知识点_lambda,initializer_list

news/2025/12/10 8:25:56/文章来源:https://www.cnblogs.com/gccbuaa/p/19329183

本文内容:lambda,initializer_list

文章目录

  • 第15章 C++重要知识点
    • 15.8 lambda表达式,for_each、find_if简介
      • 15.8.1 lambda 用法简介
        • 15.8.1.1 省略返回值
        • 15.8.1.2 省略方式
      • 15.8.2 捕获列表
        • a) [] 为空情况
        • b) [&] 捕获外部作用域中所有变量,并作为引用在函数体内使用
        • c) [=]
        • d) [this] 捕获类中this指针
        • e ) [变量名] : 捕获外部指定的变量名
        • f) [=,&变量名]
        • g) [&,变量名] 按照引用捕获所有的变量,按值捕获后边的变量名
      • 15.8.3 lambda表达式延迟调用易出错的细节分析
      • 15.8.4 lambda 表达式中 mutable (易变的)
      • 15.8.5 lambda 表达式的类型及存储
      • 15.8.6 lambda 与 for_each 和 find_if
        • 5.8.6.1 for_each 和 lambda表达式
        • 5.8.6.2 find_if
      • 15.8.7 lambda表达式的应用实践
        • 实例1 :map键值对中加入 function函数对象
        • 实例2:智能指针与lambda表达式
        • 实例3:priority_queue 与 lambda表达式的使用
      • 15.8.8 lambda表达式捕获模式的陷阱分析和展示
        • 5.8.8.1 捕获列表中的地址符&
        • 5.8.8.2 形参列表中使用 auto
        • 5.8.8.3 成员变量的捕获问题
    • 15.9可变参数函数,initializer_list,省略号形参
      • 15.9.1 可变参函数
      • 15.9.2 initializer_list(初始化列表)
        • 5.9.2.1 使用大括号 { } 传参
        • 5.9.2.2 initializer_list拷贝和赋值
        • 5.9.2.4 initializer_list使用场景
      • 15.9.3 省略号形参 (...) 三个点

第15章 C++重要知识点

15.8 lambda表达式,for_each、find_if简介

15.8.1 lambda 用法简介

lambda 是C++ 11 引入,是一个匿名函数,可以捕获一定范围内的变量。
特点:
a) 是个匿名函数,也可以理解为“可调用的代码单元”,或者理解为未命名的内联函数;
b) 它也有一个返回类型,一个参数列表,一个函数体;
c) 与普通函数不同的是lambda 表达式可以在函数内部定义。
格式:
[捕获列表] (参数列表)-> 返回类型{函数体};
因为很多时候,lambda表达式返回值特点明显,所以允许lambda表达式返回类型省略,编译器自动推导;

15.8.1.1 省略返回值
auto f2 = [](int a) {
return a + 1;
};
cout << f2(1) << endl;

编译器并不是总能推断出返回值类型,如果编译器不能自动推导,需要写出返回值。

15.8.1.2 省略方式

大家注意:编译器并不是总能推断出返回值类型,如果编译器不能自动推导,需要写出返回值。

auto f3 = []() {return 1; };
auto f32 = [] {return 1; };

c) 捕获列表[] 和函数体不能省,必须时刻包含。
d) lambda 调用方法和普通函数相同,都是使用 () 这种函数调用运算符。
e) lambda 表达式可以不返回任何类型,不返回任何类型就是void

auto f4 = [] {};

15.8.2 捕获列表

通过捕获列表来捕获一定范围内的变量.

a) [] 为空情况

[]为空表示不捕获任何变量; 如果是静态变量可直接使用。
在lambda表达式中,可以直接使用外部的静态变量。

static int i = 9;
auto f1 = [] {
return i;		//直接在lambda中使用静态变量。
};
b) [&] 捕获外部作用域中所有变量,并作为引用在函数体内使用
int i = 9;
auto f5 = [&]
{
i = 5; // 因为&的存在,那么久允许给I赋值,从而也就改变了i的值;
return i;
};
cout << f5() << endl;
cout << i << endl;
c) [=]

捕获外部作用域中所有变量,并作为副本(按值)在函数中使用,也就是可以用它的值,但不允许给它赋值。

int i2 = 9;
auto f6 = [=] {return i2;  };
d) [this] 捕获类中this指针

一般用于类中,捕获当前类中this指针,让Lambda表达式和当前类成员函数同样的访问权限。如果 [] 中已经使用过了 & 或者 = ,那么默认就已经使用了this, 说白了,捕获this的目的就是为了在lambda中使用当前类的指针。

e ) [变量名] : 捕获外部指定的变量名

如果是多个变量名,彼此之间用 , 分割,[变量名] 表示按值捕获,[=]则是捕获所有的变量,比=更精准的捕获。
按引用捕获变量名[&变量名]。

f) [=,&变量名]

=表示按值捕获所有外部变量,但是按引用捕获&中所有的变量,这里等号必须写在开头位置。也就是说,这个捕获列表,第一个位置表示的是默认捕获方式,后续其他的都是显示捕获方式。

g) [&,变量名] 按照引用捕获所有的变量,按值捕获后边的变量名

15.8.3 lambda表达式延迟调用易出错的细节分析

int x = 5;
// auto f7 = [=]
auto f7 = [&]
{
return x;
};		// 
x = 10;
cout << f7() << endl;  // 5 , 因为将5 输入到了lambda表达式中;

预期的结果是10,实际上是5.

15.8.4 lambda 表达式中 mutable (易变的)

[=]以值方式捕获外部变量时候,加上mutable就可以修改该值了。注意,加mutable时候不能省略()。可以修改该值,但是该值是原来的副本,所以对原来的值没有影响。

int x2 = 5;
auto f8 = [=]() mutable	// 注意要加mutable ,则()就可以省略了
{
x2 = 6;
return x;
};

15.8.5 lambda 表达式的类型及存储

C++11 中 Lambda 表达式的类型被称为 闭包类型 closure type;
闭包:函数内的函数(可调用对象),正常不能在函数中定义函数,本质上就是Lambda表达式创建的运行时期的对象。
lambda表达式是一种比较特殊的,匿名的,类类型[闭包类]的对象(也就是定义了一个类的类型,又生成了一个匿名的类对象)
所以,我们可以用std::function 和 std::bind 来保存和调用lambda表达式,每个Lambda都会编译器给咱们生成lambda表达式这种语法,是我们可以就地定义匿名函数;

std::function<int(int)> fc1 = [](int tv) {return tv; };cout << fc1(15) << endl;std::function<int(int)> fc2 = std::bind([](int tv) {return tv;}, 15);cout << fc2(15);int a[5];a[0] = 1;  // 便捷写法 (语法糖)*(a + 1) = 3;

15.8.6 lambda 与 for_each 和 find_if

5.8.6.1 for_each 和 lambda表达式

for_each是个函数模板;在for_each的第三个参数中使用一个labmda表达式,可以对原始的数组进行修改。

void test03()
{
vector<int> myvector = { 1,2,3,4,5,6 };// for_each(myvector.begin(), myvector.end(), myfunc);for_each(myvector.begin(), myvector.end(), [](int &v) {cout << v << " "; });cout << endl;}
5.8.6.2 find_if

用来查找一个东西,取决于它的第三个参数,它的第三个参数也是个函数对象。

vector<int> myvector2 = { 10,202,30 };auto result = find_if(myvector2.begin(), myvector2.end(), [](int val) ->bool{cout << endl;cout << val << "	";return true;// 只要返回false, 那么find_if 就不停地便利myvector, 一直返回true或者便利完为止。});if (result == myvector2.end()){cout << "没找到" << endl;}else{cout << "找到了" << endl;}

15.8.7 lambda表达式的应用实践

Lambda表达式就是一个函数对象,可以方便的表示一元函数对象 和 二元函数对象。
Lambda表达式只能使用在语句中,如果想跨语句使用之前定义好的lambda表达式,怎么办呢? 用什么类型来表示lambda表达式呢?
当然是function类型来表示函数对象类型了,

实例1 :map键值对中加入 function函数对象
int main()
{
map<int, function<int(int, int)>> cacul;cacul[1] = [](int a, int b)->int {return a + b; };cacul[2] = [](int a, int b)->int {return a - b; };cacul[3] = [](int a, int b)->int {return a * b; };cacul[4] = [](int a, int b)->int {return a / b; };cout << "10 + 12" << cacul[1](10, 12) << endl;system("pause");return 1;}
实例2:智能指针与lambda表达式

当使用FILE 句柄时,自动调用第二个参数lambda表达式。

unique_ptr<FILE, function<void(FILE *)>> ptr1(fopen("data.txt", "w"), [](FILE *pf) {fclose(pf); });
实例3:priority_queue 与 lambda表达式的使用

不使用lambda表达式需要在Data类中提供 < > 运算符重载函数。
priorty中使用lambda表达式之后

using FUNC = function<bool(Data&, Data&)>;
priority_queue<Data, vector<Data>, FUNC>maxHeap([](Data &d1, Data &d2)->bool{return d1.mb > d2.mb;});maxHeap.push(Data(10, 20));maxHeap.push(Data(15, 15));maxHeap.push(Data(20, 10));

15.8.8 lambda表达式捕获模式的陷阱分析和展示

5.8.8.1 捕获列表中的地址符&

lambda表达式用于捕获外部变量时候,如果lambda [&]时候,一定要注意外部变量的生命周期。srand() 表示生成一个随机数种子,当调用 rand() 时会产生一个不一样的值。

std::vector<std::function<bool(int)>> gv;void myfunc(){srand((unsigned)time(NULL));int tmpvalue = rand() % 6;		// 产生0-5 之间随机值。gv.push_back([&](int tv) {if (tv % tmpvalue == 0)  // 问题:当被调用时候return true;return false;});}

捕获函数中的变量,当函数结束的时候,释放栈空间,对应的变量也就结束了。

cout<xgv[0](10)<<end1;//跟踪调试,这个调用导致问题,因为此时1ambda里tmpvalue已被销myfunc( );//毁,所以会产生未定义行为

针对上述代码设置断点并跟踪调试,会发现当调用gv0来执行lambda表达式中的代码时,其中的tmpvae已经是一个无效值,所以程序执行后会产生未定义行为(结果错误甚至程序崩溃)。

5.8.8.2 形参列表中使用 auto

C++14 中允许在lambda表达式的形参数列表中。
将上面的lambda中的[&] 修改为 [=] ,程序就不会崩溃。

void test3()
{
std::vector<std::function<bool(int)>> gv;srand((unsigned)time(NULL));int tmpvalue = rand() % 6;		// 产生0-5 之间随机值。gv.push_back([=](auto tv) {if (tv % tmpvalue == 0)  // 问题:当被调用时候,return true;return false;});}

上面代码中,lambda表达式中按值捕获的方式解决了因为变量tmpvalue超出作用域范围(又叫引用悬空)而产生的问题,因为按值捕获的方式在lambda表达式里持有的是tmpvalue的值(副本),而且是在lambda表达式定义的那行就已经持有该值了。也就是说myfunc 一被调用,lambda表达式中就持有了tmpvalue的值。
想象一下,tmpvalue是一个int类型的值还好办,如果是一个指针,那么就算是在lambda表达式中持有的是指针的副本(指针副本和原指针指向的是同一块内存),但如果后续把这个指针指向的内存 delete掉,那么再次调用该lambda表达式同样面临lambda 表达式中该指针副本指向的内存已经无效的问题,因为这段内存已经被系统回收了。所以这务必要引起程序员的注意,不要犯类似的错误。

5.8.8.3 成员变量的捕获问题

在类中lambda表达式使用[=] 或者 [this] 捕获类的成员变量。生成类的成员属性的副本;在类中,lambda 表达式执行成功与否,取决于pat对象是否存在,只有pat对象存在,这个lambda表达式执行才正确。

15.9可变参数函数,initializer_list,省略号形参

15.9.1 可变参函数

能够接受非固定的个数参数就是可变参类型;initializer_list 标准库类型,该类型能够使用的前提条件是所有的实参类型相同。

15.9.2 initializer_list(初始化列表)

如果一个函数,它的实参数量不可预知,但是所有参数类型相同,我们就可以使用这个initializer_list 类型的形参来接收。
initializer_list 是C++11提供的新类型,也是一个类模板 vector;我们把initializer_list 理解成某种类型数组,这个类模板里制定的类型模板参数就是这个数组里保存的数据类型。

initializer_list<int> myarray;		// 数组,元素类型是int,空列表initializer_list<int> myarray2 = { 1,2,3,4,5 };

注意:initializer_list 队形中的元素,永远是常量值,不能改变。
C++11 将使用大括号{}的初始化{初始化列表}作为一种比较通用的初始化方式,注意积累。

5.9.2.1 使用大括号 { } 传参

若要向initializer_list 形参传递一个序列,则必须把这个序列放在{}中包括起来作为一个整体;

printvalue({ "aa","bb" });
5.9.2.2 initializer_list拷贝和赋值

拷贝,赋值一个initializer_list 对象,不会拷贝列表中的元素,原来对象拷贝或者赋值出来的对象共享列表中的元素。
myarray3 中的对象只是 元素的引用,并不真正拥有这些数据。

initializer_list<string> myarray3 = { "aa","bb","cc" };initializer_list<string> myarray4(myarray3);initializer_list<string> myarray5;myarray5 = myarray4;
5.9.2.4 initializer_list使用场景

std::initializer_list 使用场景:

// 方式1:
void test()
{
// 1 使用 std::initializer_list 方式初始化类容器
std::vector<int> v = { 1, 2, 3, 4, 5 };std::list<std::string> l1 = { "apple", "banana", "cherry" };}// 方式2:构造函数和赋值class MyClass{public:MyClass(std::initializer_list<int> list){for (auto& value : list){cout << value << endl;}}};void test2(){MyClass obj = { 1, 2, 3, 4 };}// 方式3:作为函数参数,可以接受任意数量的同类型参数void print(std::initializer_list<int> list){for (auto elem : list) {std::cout << elem << ' ';}std::cout << '\n';}void test3(){print({ 1,2,3,4,5,6 });  // 输出: 1 2 3 4 5 6}// 方式4:聚合类初始化// 无构造函数,无私有或保护的非静态成员;无基类和虚函数;无私有或保护的继承// 什么是聚合类?聚合类的一个主要特点是 花括号初始化语法进行初始化// 聚合初始化允许你直接初始化对象的数据成员,而不需要调用构造函数。struct Point{int x, y;};void test4(){Point p = { 1,2 };cout << p.x << endl;cout << p.y << endl;}

15.9.3 省略号形参 (…) 三个点

省略号形参一般无法正确处理类类型对象,能正确处理的int,char * ;这种省略号形参形式的可变参函数,虽然参数数量不固定,但是函数的所有参数是存储在先行连续的栈空间中。而且带 … 的可变参 函数必须至少有一个普通函数,我们就可以通过这个普通参数来寻址后续的所有的可变参类型以及值。

cout << average(3, 1, 2, 3, 4);
int average(int num, ...)
{
va_list valist;  // 创建一个va_list 类型的变量;
int sum = 0;
va_start(valist, num);  // 是valist 指向一个起始参数
for (int i = 0; i < num; ++i)
{
// 遍历参数 
sum += va_arg(valist, int);  // 参数2,说明返回类型是int类型。
}
va_end(valist);
return sum;
}
#include <stdarg.h>

用到的宏

va_list valist;  				// 创建一个va_list 类型的变量;
va_start(valist, num);  		// 是valist 指向一个起始参数
sum += va_arg(valist, int);
va_end(valist);

实现类似printf

funcTest("5", "a", "b", "c", "d", "e");
//void funcTest(const char *msg, ...)  // 2 
void funcTest(const char *msg ...)
{
va_list valist;			// 创建一个 va_list 变量
int csgs = atoi(msg);   // 这个就是拿到可变参数量,这个数量我们后续要用于获取可变参数。
// atoi 将 char * 转为 int类型
va_start(valist, msg);	// 使 valist指向起始参数
int paracount = 0;
while (paracount < csgs)
{
char *p;  // 这块我们假定给的可变参都是字符串;
p = va_arg(valist, char *);
printf("第%d个参数是:%s\n", paracount, p);
paracount++;
}
va_end(valist);
}

注意事项:

1 至少有一个有效形参,形参不能是完全 ...2 ... 只能出现在形参列表最后一个位置;void funcTest(const char *msg, ...)
3 ... 之前的逗号,可以省略;
4 如果有多个普通参数,va_start(valist,,msg),第二个参数必须... 之前的那个参数
// void funcTest(const char *msg ,const char *msg2 ...)  va_start(valist,msg2)
5 一般这些可变参类型是数值型或者字符串类型还能正常处理,其他类型一般都不能正常处理,所有 ... 使用场合不多。
6 不建议在C++中使用 ... 但是遇到了要知道。

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

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

相关文章

2025年南通装修报价方案排行榜,精准装修预算报价服务商推荐 - 工业品牌热点

装修预算超支、报价明细模糊、增项陷阱频发——这些困扰无数业主的装修痛点,根源往往在于报价不透明与需求错配。为帮助南通业主避开预算雷区,锁定能提供精准装修预算报价、全透明装修报价方案的靠谱服务商,我们从报…

2025年度螺旋卸料离心机/卧式螺旋卸料沉降离心机实力厂家排 - mypinpai

固液分离是化工、环保、食品等行业的核心工序,2024年国内离心分离设备市场规模突破1200亿元,年增速达32%。但行业调研显示,38%的企业因设备选型不当导致分离效率低、维护成本高——如螺旋卸料离心机与卧式螺旋卸料沉…

上班摸鱼玩塔防,竟然是为了学架构?「GitHub 热点速览」

如果你的生产环境用了 NextJS——基于 React 的流行开源 Web 开发框架,那上周末可能过得并不轻松。受 React 高危漏洞的影响,攻击者可以通过 HTTP 请求攻击 NextJS 项目,远程执行任意指令。如果你不确定项目是否受影…

2025年质量好的CNC亚克力产品加工厂家信誉综合榜(权威) - 行业平台推荐

2025年质量好的CNC亚克力产品加工厂家信誉综合榜(权威)行业背景与市场趋势随着制造业的数字化转型和消费升级,CNC亚克力加工行业迎来了前所未有的发展机遇。亚克力材料以其优异的透明度、耐候性和加工性能,在广告标…

springboot获取post请求参数

springboot获取post请求参数在Spring Boot中,获取POST请求参数的方法取决于你使用的数据绑定方式。以下是几种常见的方式: 1. 使用@RequestParam 如果你的POST请求的参数在URL的查询字符串中(例如,POST /example?…

2025年知名的环氧防腐涂料新厂实力推荐榜(最新) - 品牌宣传支持者

2025年知名的环氧防腐涂料新厂实力推荐榜()行业背景与市场趋势随着全球工业化的持续推进和基础设施建设的加速,环氧防腐涂料市场正迎来前所未有的发展机遇。据行业报告显示,2024年全球环氧防腐涂料市场规模已达到约…

从客服到知识库:哪些 NLP 生成式 AI 模型真正能在中国落地? - 品牌排行榜

一|为何中国企业在 2025 年重新聚焦 NLP 大模型? 在过去一年里,“实现内容可理解”“让文档能应答”“使客服自动回复”“让知识库具备对话功能”,已成为中国企业最优先推进的智能化工作。 而支撑这些应用场景的核…

sqlite 添加用户密码的方法

sqlite 添加用户密码的方法1、首先添加引用 2、然后替换Bin下的x86/x64文件夹下的文件 3、添加修改密码的代码public bool SetSqlitePsd(string passWord) {bool result = false;try{using (SQLiteConnection cnn = ne…

预算吃紧,也要先跑起来:哪些生成式 AI 平台提供真正可用的免费试用? - 品牌排行榜

一|为何 “免费试用” 成为中国企业落地生成式 AI 的首要选择? 在过去一年里,越来越多中国企业将生成式 AI 融入业务流程,像智能客服、内部知识库搭建、营销内容生成、RAG 系统开发、行业 Agent 构建、预测与自动化…

从文档到示例代码:2025 中国开发者值得关注的生成式 AI 平台 - 品牌排行榜

一、为何文档质量成为中国开发者选择生成式 AI 平台的决定性因素? 过去一年,中国开发者在搭建 RAG(检索增强生成)、聊天助手、企业知识库、智能客服、营销自动化、行业 Agent 等系统时,面临的核心问题并非模型能力…

完整教程:【数据结构】算法题

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

深度剖析!2025俄罗斯电商选品工具大盘点 - 栗子测评

深度剖析!2025俄罗斯电商选品工具大盘点!在竞争日益激烈的俄罗斯电商市场中,高效、精准的选品与运营工具已成为卖家突围的关键。一批专注细分领域、功能独特的新兴服务商正逐渐崭露头角。本文从实用性、创新性和市场…

实用指南:Bash Shell脚本学习——唇读数据集验证脚本

实用指南:Bash Shell脚本学习——唇读数据集验证脚本2025-12-10 08:13 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; di…

2025年口碑好的水性涂料/水性聚氨酯涂料优质厂商精选榜(口碑优) - 行业平台推荐

2025年口碑好的水性涂料/水性聚氨酯涂料优质厂商精选榜(口碑优)行业背景与市场趋势随着全球环保法规日益严格和消费者环保意识提升,水性涂料市场正迎来前所未有的发展机遇。水性涂料以水为分散介质,相比传统溶剂型…

2025浙江软膜天花厂家怎么选?这份实力厂商清单精准锁定 - 栗子测评

2025浙江软膜天花厂家怎么选?这份实力厂商清单,精准锁定!在建筑装饰行业追求美观与功能兼具的今天,软膜天花以其独特的灵活性、丰富的造型和优异的物理性能,正成为设计师青睐的材料。浙江地区作为我国装饰材料产业…

2025义乌拉链头厂家哪家正规?合规厂商盘点 - 栗子测评

2025义乌拉链头厂家哪家正规?合规厂商盘点。在义乌这个拉链产业集聚地,选择正规的拉链头厂家是保障采购质量和合作安全的关键。正规厂家不仅能提供合格的产品,还能在合作流程、售后保障等方面让人放心。以下为大家推…

2025年度全纸桶制造商TOP5权威推荐:看哪家全纸桶价格合 - myqiye

在医药中间体、原料药、油脂、食品等行业的仓储与运输环节中,全纸桶作为核心包装载体,其质量稳定性、定制灵活性直接影响企业供应链效率与成本控制。2024年华中地区工业包装市场数据显示,全纸桶需求年增速达32%,但…

2025年矿物干燥剂厂家综合实力排行榜:权威测评与采购指南 - 2025年11月品牌推荐榜

文章摘要 本文基于中国包装联合会权威数据,通过对全国200余家矿物干燥剂生产企业的综合测评,从产品品质、服务能力、供应链稳定性、客户满意度四大维度深度分析行业现状。重点推荐合肥亨美泰工业包装有限公司等5家优…

从数据到洞察:2025 中国企业可采用的实时生成式 AI 工具盘点 - 品牌排行榜

一、为何实时数据分析成为中国企业的刚需? 在 2025 年的数字化竞争格局中,中国企业的关注点已从 “是否拥有数据” 转变为 “能否实时理解数据并立即采取行动”。 无论是制造、零售、金融、能源等核心行业,还是客服…

2025年靠谱的纯水加热器/在线纯水加热器厂家销量TOP榜(畅销榜) - 品牌宣传支持者

2025年靠谱的纯水加热器/在线纯水加热器厂家销量TOP榜(畅销榜)行业背景与市场趋势随着光伏、半导体、PCB等高科技产业的快速发展,对高纯水加热设备的需求呈现爆发式增长。纯水加热器作为工业生产中不可或缺的关键设…