C++11——Lambda匿名函数

本文深入探讨C++11的新特性,重点讲解Lambda表达式和匿名函数的使用。从基础概念、语法格式到实际应用,包括参数、返回类型、捕获外部变量等,通过实例展示了Lambda在STL中的应用以及如何与外部变量交互。此外,还介绍了Lambda表达式的高级用法,如修改权限和异常处理。学习C++11的Lambda特性,能有效提升编程效率和代码质量。
摘要由CSDN通过智能技术生成

系列文章目录

C++11新特性大全+实例


前言

C++ 这门编程语言的历史可以追溯至 1979 年,当时的 Bjarne Stroustrup(C++ 之父,后续简称 Stroustrup)还在使用 Simula 语言进行开发工作。

1998 年,C++ 标准委员会发布了第一版 C++ 标准,并将其命名为 C++ 98 标准。据不知名人士透露,《带注释的C++参考手册》这本书对 C++ 98 标准的制定产生了很大的影响。

经过作者的不断迭代,一本书往往会先后发布很多个版本,其中每个新版本都是对前一个版本的修正和更新。C++ 编程语言的发展也是如此。截止到目前(2020 年),C++ 的发展历经了以下 3 个个标准:

2011 年,新的 C++ 11 标准诞生,用于取代 C++ 98 标准。

2014 年,C++ 14 标准发布,该标准库对 C++ 11 标准库做了更优的修改和更新;

2017 年底,C++ 17 标准正式颁布;

虽然学习 C++11 需要花些时间,但这是非常值得的;C++11 非常实用,它不但提高了开发效率,还让程序更加健壮和优雅。程序员应该乐于升级换代已有的知识,而学习和使用 C++11 早就是大势所趋。

|版本声明:山河君,未经博主允许,禁止转载

一、Lambda表达式

1.作用

Lambda表达式的定义其实不用多说,就是隐式的表达一个函数。但有人会觉得好端端的,干啥非得把函数名给隐式掉,显示它不香么?

Lambda使用场景多为在STL库中,有时必须显示创建一个函数接口用于调用,下面会举例子,又或者在某个类中,必须要创建一个接口用于功能(例如字符格式转换),但是放在类中和类功能就不太合适,专门放在一个功能类里又没有别的地方使用,这个时候就可以使用匿名函数。

2.表达式格式

一个完整的Lambda匿名函数表达式如下:

[capture list] (params list) mutable exception-> return type { function body }

选项含义
capture list捕获外部变量列表
params list形参列表
mutable用于是否可以修改捕获的变量
exception异常设定
return type返回类型
function body函数体

是不是看起来很复杂,以上除了capture list和function body是不可省略的,其它的都可以跟据使用需求进行省略,经常使用的格式有以下几种:

格式使用
[capture list] (params list) -> return type {function body}有参数、有返回值
[capture list] (params list) {function body}有参数、无返回值
[capture list] {function body}无参数、无返回值

是不是还是感觉很复杂,不必担心,我们直接看一下使用方式

二、匿名函数基础使用

1.最简单用法

其实最简单的格式为

[capture list] {function body}

如下:

[]{cout << 1;}

函数体只输出,不传参,并且没有返回值。

2.简单使用

STL中有个sort函数用于进行排序,一般默认进行升序,也可以指定排序方式,如下:

bool compare(int x, int y)
{
	return x > y;
}

int main(int argv, char* argc[])
{
	int nArray[] = { 1,34,63,234,546,123,35,23 };
	sort(nArray, nArray + sizeof(nArray) / 4, compare);
	return 0;
}

有没有感觉如果这样写,加如是在类中或者其他地方调用,函数作用不伦不类,也很繁琐,如果是用匿名函数,格式为

[capture list] (params list) -> return type {function body}

int main(int argv, char* argc[])
{
	int nArray[] = { 1,34,63,234,546,123,35,23 };
	sort(nArray, nArray + sizeof(nArray) / 4, [](int x, int y)->bool { return x > y; });
	return 0;
}

3.获取匿名函数指针添加函数名

上述例子就可以看出,匿名函数虽然没有函数名,但是实际在sort使用时候,还是传入了函数指针,那么既然我们能获取到函数指针,我们就可以赋予匿名函数一个函数名。

auto print = [](int x)->bool {cout << x << endl; };

int main(int argv, char* argc[])
{
	print(10);
	return 0;
}

三、外部变量的使用

1.外部变量使用格式

外部变量格式功能
[]空方括号表示当前 lambda 匿名函数中不导入任何外部变量
[=]只有一个 = 等号,表示以值传递的方式导入所有外部变量
[&]只有一个 & 符号,表示以引用传递的方式导入所有外部变量
[val1,val2,…]表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序
[&val1,&val2,…]表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序
[val,&val2,…]以上 2 种方式还可以混合使用,变量之间没有前后次序
[=,&val1,…]表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入
[this]表示以值传递的方式导入当前的 this 指针

估计看到这里,有人会不明白什么是外部变量,我们为什么要结合外部变量一起使用?这是因为在实际使用时候,很多情况下我们无法传入我们想传入的参数,例如STL中遍历容器函数for_each:

int main(int argv, char* argc[])
{
	vector<int> vecTest = { 1,2,3,4,5,6 };
	for_each(vecTest.begin(), vecTest.end(), [](int i)->void {if (i < 5) cout << " " << i; });

	return 0;
}

该段代码是遍历容器中小于5的值并且进行打印,但是这个时候如果不想打印小于5而是根据条件打印小于x的数值,这样写就不满足了,这个时候就需要直接使用外部变量,例如:

int main(int argv, char* argc[])
{
	int x = 4;
	vector<int> vecTest = { 1,2,3,4,5,6 };
	for_each(vecTest.begin(), vecTest.end(), [x](int i)->void {if (i < x) cout << " " << i; });

	return 0;
}

2.外部变量的使用

以下几种可以混合使用

(1)值捕获

int main(int argv, char* argc[])
{
	int a = 4;
	auto fun = [a]{cout << a << endl; };

	return 0;
}

(2)引用捕获

int main(int argv, char* argc[])
{
	int a = 4;
	auto fun = [&a]{cout << a << endl; };

	return 0;
}

(3)隐式捕获

除了值捕获和引用捕获,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。

int main(int argv, char* argc[])
{
	int a = 4;
	int j = 5;
	auto fun1 = [=]{cout << a << " " << j << endl; };
	auto fun2 = [&] {cout << a << " " << j << endl; };

	return 0;
}

四、进阶用法

1.修改权限

上文说过mutable用于是否可以修改捕获的变量,为什么存在是否可以修改捕获变量这个问题呢,是因为捕获变量要么是值传递、要么是引用传递,引用传递当然可以进行修改变量,那么有的时候如果不使用引用实际上是同样可以进行修改的。

局部变量:

	int a = 10;
	auto fun1 = [a] {a++; }; //编译报错

全局变量:

int all_num = 10;

int main(int argv, char* argc[])
{
	auto fun1 = [] {all_num++; }; //编译通过
}

引用变量:

int a = 10;
auto fun1 = [&a] {a++; }; //编译通过

借助mutable

int a = 10;
auto fun1 = [a] ()mutable { a++; }; //编译通过

2.异常抛出

我们知道基本任何函数都可以进行抛出异常,同样的匿名函数也可以,如下两种写法都可以:

//显示的说明可以抛出异常
auto except = []()throw(int) {
        throw 1;
    };
    try {
        except();
    }
    catch (int) {
        cout << "捕获到异常";
    }
//隐式的
    auto except1 = []{
        throw 1;
    };
    try {
        except1();
    }
    catch (int) {
        cout << "捕获到异常";
    }

但是如果明确不抛出异常,那么如下代码会段错误

auto except1 = []()noexcept{
        throw 100;
    };

总结

仍在持续更新中~
如果对您有所帮助,请点个赞!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值