C++11中Lambda的使用

3 篇文章 0 订阅
2 篇文章 0 订阅
C++11标准中新增了lambda函数

关于lambda函数,百度百科中的解释是这样的:

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。

在C++11前,为实现一个让奇数排在前面,偶数排在后面,并且从小到大排列的程序可能要这样写:

// 编译环境:MinGW-W64 8.1.0
#include <iostream>
#include <algorithm>
using namespace std;
bool fun(const int &a, const int &b) {
	if ((a % 2 == 1) && (b % 2 == 0))
		return true;
	if (a < b) return true;
	return false;
}
int main() {
	int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	sort(a, a + 10, fun);
	for (int i = 0; i < 10; i++)
		cout << a[i] << ' ';
    // 输出:1 3 5 7 9 2 4 6 8 10 
	return 0;
}

可以看到,我们定义了一个函数fun并实现了这个排序,但真的有必要再写一个完整的全局函数吗?
C++11中的lambda就很好地解决了这一问题,我们先来看一段代码:

#include <iostream>
using namespace std;
int main() {
    auto f = [](int x){ return x * x; };
    cout << f(20) << endl;
    return 0;
}

我们用auto声明了一个变量f,并将一个长成这样的东西赋给了它:[](int x){ return x * x; }
这个就是lambda。
lambda的格式:
[捕获列表]<模板声明>(参数列表)mutable 异常说明->类型{函数体}
其中:

格式内容
捕获列表允许访问当前作用域下的某一个(些)变量
模板声明与普通函数的模板相同(但省略template关键字)
参数列表
(可选)
lambda函数的参数,若无参数,可以连括号一起省略(当然也可以保留,不过没必要),不能有不定参数,不能有默认参数,参数必须有名称(C++14标准允许以auto作为参数类型)
mutable
(可选)
指定捕获列表以值传递的变量可以被修改,但不会改变变量本身(相当于普通函数的值传递),引用传递的变量不变
(捕获列表的变量传递方式见下表)
异常说明
(可选)
指定lambda可能抛出的异常,例如throw(int)noexcept
类型
(可选)
指定返回值类型,包括void类型(如果返回类型比较明显,可以省略,让编译器自动推断,省略时要连同箭头->一起省略)
如果省略了参数列表的括号,那么不能指定此项
函数体函数中要执行的代码,如果没有return语句且没指定类型,默认返回类型为void

捕获列表语法:

表达式作用
[]什么也不捕获
[var]捕获当前作用域var变量,值传递(不能修改捕获变量值)
[&var]捕获当前作用域var变量,引用传递(可以修改捕获变量值)
[=]捕获当前作用域所有变量,值传递
[&]捕获当前作用域所有变量,引用传递
[=,&var]捕获当前作用域所有变量,以值传递,但以引用传递var(也可以使用[&,var]来进行值传递)
[this]捕获class的this指针,如果捕获列表指定了[=]或[&],则会自动传递this
(PS:不能捕获成员变量,只能捕获this指针,用this指针访问成员变量)

另外,捕获列表可以传递多个变量,以逗号分隔

// 最短的lambda函数
[]{}

一个模板例子

[]<class T>(T v) noexcept -> void{
    cout << v << endl;
}("blog.csdn.net");

所以,我们就可以用lambda来实现刚刚的sort里的比较函数:

// 编译环境:MinGW-W64 8.1.0
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
	int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	sort(a, a + 10, [](const int &a, const int &b){
		if ((a % 2 == 1) && (b % 2 == 0))
			return true;
		if (a < b) return true;
		return false;
	});
	for (int i = 0; i < 10; i++)
		cout << a[i] << ' ';
    // 输出:1 3 5 7 9 2 4 6 8 10 
	return 0;
}

接下来是踩坑环节!

#include <iostream>
using namespace std;
int foo(int (*f)(int), int x) {
    return f(x);
}
int main() {
    cout << foo([](int a)->int{ return a * 5 + 7; }, 2) << endl;
    // 输出 17,一切正常
    int Integer = 123;
    cout << foo([Integer](int a)->int{ return a * 5 + Integer; }, 2) << endl;
    // 编译错误
    return 0;
}

错误信息:
错误信息
可以看到,我们的[](int a)->int{ return a * 5 + 7; }可以隐式转换为int (*)(int)类型来传递,但添加了捕获参数的lambda函数不能转换为函数指针。

解决方法:

  • 使用模板参数(因为lambda的本质是一个临时对象)。
  • 使用functional头文件封装的std::function类型。

正确改法:

  1. 模板参数
#include <iostream>
using namespace std;
template<class Fn>
int foo(Fn f, int x) {
    return f(x);
}
int main() {
    cout << foo([](int a)->int{ return a * 5 + 7; }, 2) << endl;
    // 输出 17
    int Integer = 123;
    cout << foo([Integer](int a)->int{ return a * 5 + Integer; }, 2) << endl;
    // 输出 133
    return 0;
}
  1. std::function
#include <iostream>
#include <functional>
using namespace std;
int foo(function<int(int)> f, int x) {
    return f(x);
}
int main() {
    cout << foo([](int a)->int{ return a * 5 + 7; }, 2) << endl;
    // 输出 17
    int Integer = 123;
    cout << foo([Integer](int a)->int{ return a * 5 + Integer; }, 2) << endl;
    // 输出 133
    return 0;
}
  • 28
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
C++11,可以使用lambda表达式来定义一个匿名函数。Lambda表达式通过方便的方式,将函数对象的定义和使用结合在一起,非常适合用于一些简单的函数对象的定义。 Lambda表达式的语法如下: ``` [capture](parameters)->return_type{body} ``` 其: - `capture`表示捕获列表,用于捕获上下文的变量。 - `parameters`表示参数列表,表示该lambda表达式所接受的参数。 - `return_type`表示返回类型,可以省略,表示由编译器自动推断。 - `body`表示函数体,表示该lambda表达式所执行的操作。 例如,下面的代码定义了一个lambda表达式,用于计算两个整数的和: ```c++ auto sum = [](int a, int b) -> int { return a + b; }; cout << sum(1, 2) << endl; // 输出3 ``` 在lambda表达式,我们可以使用`[]`来指定捕获列表,其包括: - `[]`:不捕获任何变量。 - `[&]`:以引用的方式捕获所有变量。 - `[=]`:以值的方式捕获所有变量。 - `[a, &b]`:以值的方式捕获变量a,以引用的方式捕获变量b。 - `[&a, b]`:以引用的方式捕获变量a,以值的方式捕获变量b。 例如,下面的代码定义了一个lambda表达式,捕获了外部变量a和b,并以值的方式进行捕获: ```c++ int a = 1, b = 2; auto sum = [a, b]() -> int { return a + b; }; cout << sum() << endl; // 输出3 ``` Lambda表达式还可以作为函数参数进行传递,例如: ```c++ void print(int n, function<int(int)> func) { for (int i = 1; i <= n; i++) { cout << func(i) << " "; } cout << endl; } int main() { int n = 5; print(n, [](int x) -> int { return x * x; }); // 输出1 4 9 16 25 return 0; } ``` 在上面的代码,我们定义了一个`print`函数,接受一个整数`n`和一个函数对象`func`作为参数。在`main`函数,我们调用`print`函数,并将一个lambda表达式作为`func`参数传递进去,该lambda表达式接受一个整数参数并返回其平方。最终,`print`函数会输出1 4 9 16 25。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jcShan709

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值