C++11 Lambda表达式
Lambda表达式
Lambda表达式是 C++11 时引入的特性。
参考学习资料:
仿函数(functor) 与 Lambda
Lambda表达式又被称作匿名函数,本质是一个特殊的,匿名的类类型。它是一个带有 operator()
的类,即仿函数。
仿函数,就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator()
,这个类就有了类似函数的行为,就是一个仿函数类了。
#include <iostream>
class AddNum{
public:
AddNum(int num) : num_(num) {}
int operator()(int x) const {
return num_ + x;
}
private:
int num_;
};
int main(){
// functor
auto add_num = AddNum(10);
auto x = add_num(5);
std::cout << "x:" << x << std::endl;
return 0;
}
#include <iostream>
int main(){
// Lambda
auto add_num = [num_ = 10](int x){ return num_ + x; };
auto x = add_num(5);
std::cout << "x:" << x << std::endl;
return 0;
}
以上两个例子,都是为了实现 num_ + x
的效果。
第一个例子模拟了第二个例子Lambda表达式的实际运行,在使用Lambda表达式时,编译器会自动创建一个仿函数类,只不过这个类的名字我们并不知道。
同时,实现的仿函数 operator()
带有 const
关键字,表示其无法修改成员变量值,这也是下面提到的按值捕获
无法修改其值的原因。
Lambda 用法
[ capture ]( params ) opt -> ret { body; };
capture
是捕获列表,捕获一定范围内的变量[]
表示不捕获任何变量[=]
表示按值捕获外部作用域中的所有变量,作为副本在匿名函数体内使用,其副本在匿名函数体内是只读的,无法修改值[&]
表示按引用捕获外部作用域中的所有变量,可修改值
params
是参数列表,和普通函数的参数列表一样,如果没有参数参数列表可以省略不写opt
是函数选项,不需要可以省略mutable
可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身),mutable是为了突破 const 的限制而设置的exception
指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
ret
是返回值类型,在 C++11 中,Lambda表达式的返回值是通过返回值后置语法来定义的。body
是函数体,这部分不能省略,但函数体可以为空。
例 :
[=, &var]
- 按值捕获外部作用域中所有变量,并按照引用捕获外部变量var
[var]
- 按值捕获var
变量,同时不捕获其他变量[&var]
- 按引用捕获var
变量,同时不捕获其他变量[this]
- 捕获当前类中的this
指针- 让 Lambda表达式拥有和当前类成员函数同样的访问权限
- 如果已经使用了
&
或者=
,默认添加此选项
-
int x = 0; auto func1 = [=]() { return x++; }; // 报错, 按值捕获外部变量, a是只读的 auto func2 = [=]() mutable { return x++; }; // 正常运行
Lambda表达式的类型
std::function<ret( params )>
其中,std::function
在头文件 #include <functional>
。
同时,C++11 中可用 auto
关键字代替。
例 实现一个函数功能,输入一个整数,返回这个整数加一。
int res = [](int x) -> int{
return ++x;
}(6);
上述的定义形式,只能够在一处使用,若需在多处使用,可以使用 std::function
(或 auto
关键字)或和 std::bind
方法来存储和操作 Lambda表达式:
auto func1 = [](int x) -> int{
return ++x;
};
auto func2 = std::bind([](int x) -> int{
return ++x;
}, std::placeholders::_1);
// 函数调用
func1(6); func2(6);
func1(9); func2(9);
对于没有捕获变量的 Lambda表达式,其类型也可转换为函数指针
int(*)(int) func = [](int x) -> int{
return ++x;
};
// 函数调用
func(6);
func(9);
返回值 ret
在 C++11 中,Lambda表达式返回值的定义也可省略,编译器会自动推导返回值的类型;
但是,不能通过列表初始化自动推导出返回值类型。
auto func = [](int x){
return ++x;
};
auto func = [](int x){
return {x+1, x+2};
};
实例
与 algorithm
库中的 for_each
方法配合使用:
例 实现一个函数功能,输出指向 vector
中的元素 print_arr
。
使用普通函数:
#include <iostream>
#include <algorithm>
#include <vector>
// 普通函数
void print_arr(int x){
std::cout << "x" << x << std::endl;
}
int main()
{
std::vector<int> arr = {1, 3, 5, 7, 9};
std::for_each(arr.begin(), arr.end(), print_arr);
return 0;
}
使用仿函数:
#include <iostream>
#include <algorithm>
#include <vector>
// 仿函数
struct printArr
{
void operator()(int x){
std::cout << "x" << x << std::endl;
}
} print_arr;
int main()
{
std::vector<int> arr = {1, 3, 5, 7, 9};
std::for_each(arr.begin(), arr.end(), print_arr);
return 0;
}
使用 Lambda表达式:
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> arr = {1, 3, 5, 7, 9};
// Lambda表达式
std::for_each(arr.begin(), arr.end(), [](int x){
std::cout << "x" << x << std::endl;
});
return 0;
}
对比可见,使用 Lambda表达式,便捷的省去函数的声明和定义。