时间:2026-03-17 16:24
人气:
作者:admin
C++11加入了Lambda表达式,这是现代编程语言的一个特点,lambda表达式的优点:
lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。
语法形式:
[capture](params)opt->ret{body;};
其中capture是捕获列表,params是参数列表,opt是函数选项,ret是返回值类型,body是函数体;下面逐个详细介绍。
捕获列表capture:捕获一定范围内的变量,具体使用方式
[]:不捕获任何变量。
[&]:捕获外部作用域中所有变量,并作为引用在函数体内使用(按引用捕获)
[=]:捕获外部作用域中所有变量,并作为副本在函数体内使用(按值捕获),拷贝的副本在匿名函数体内部是只读的。如果想要修改需要添加函数选项mutable。
#include <iostream>
using namespace std;
void func(int x, int y) {
int a = 7;
int b = 9;
[=, &x](int z) mutable {
int c = a;
int d = x;
b++;
cout << "bb: " << b << endl;
}(88); //没有(88)就只是匿名函数的定义,并没有调用。(88)是调用。因为有参数,所有传入的参数是88,如果没有参数就是();
cout << "b: " << b << endl;
}
int main() {
func(1, 2);
system("pause");
return 0;
}
如果没有mutable变量b++就会报错。且这里是按值捕获所以这里在lambda里面的修改不会影响到外部变量。
[=,&foo]:按值捕获外部作用域中所有变量,并按照引用捕获外部变量foo。
[bar]:按值捕获bar变量,同时不捕获其他变量。
[&bar]:按引用捕获bar变量,同时不捕获其他变量。
[this]:捕获当前类中的this指针。让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,默认添加此选项。
#include <iostream>
using namespace std;
class Test {
public:
void output(int x, int y) {
auto x1 = [] {return m_number; }; //error
auto x2 = [=] {return m_number + x + y; }; //ok
auto x3 = [&] {return m_number + x + y; }; //ok
auto x4 = [this] {return m_number; }; //ok
auto x5 = [this] {return m_number + x + y; }; //error
auto x6 = [this, x, y] {return m_number + x + y; }; //ok
auto x7 = [this] {return m_number++; }; //ok
}
int m_number = 100;
};
x1:错误,没有捕获外部变量,不能使用类成员 m_number
x2:正确,以值拷贝的方式捕获所有外部变量
x3:正确,以引用的方式捕获所有外部变量
x4:正确,捕获this指针,可访问对象内部成员
x5:错误,捕获this指针,可访问类内部成员,没有捕获到变量x,y,因此不能访问。
x6:正确,捕获this指针,x,y
x7:正确,捕获this指针,并且可以修改对象内部变量的值
参数列表params:和普通函数的参数列表一样,如果没有参数参数列表可以省略不写。
auto f = [](){return 1;} //没有参数,参数列表为空
auto f = []{return 1;} //没有参数,参数列表省略不写。
函数选项opt:
有两个类型 mutable 和 exception 两种,不需要可以省略。
mutable:可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
exception:指定函数抛出的异常,如抛出整数类型的异常,可以使用throw();
返回值类型:在C++11中,lambda表达式的返回值是通过返回值后置语法来定义的。
很多时候,lambda表达式的返回值是非常明显的,因此在c++11中允许省略lambda表达式的返回。
//完整的lambda表达式定义
auto f = [](int a) -> int{
return a+10;
};
//忽略返回值的lambda表达式定义
auto f = [](int a) {
return a+10;
};
一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值的类型,但需要注意的是初始化列表不能用于返回值的自动推导。
//可以自动推导出返回值类型
auto f = [](int i){
return i;
}
//不能推导出返回值类型
auto f1 = [](){
return {1, 2}; //基于列表初始化推导返回值,error。
}
因为通过初始化列表可以初始化的类型很多,比如初始化一个数组int[],初始化一个结构体等很多,所以不能确定初始化的类型,因此需要把初始化的类型写出来。
函数体:函数的实现,这部分不能省略,但函数体可以为空。
lambda可以看作是一种 可调用对象 。
C++中的可调用对象有四种:(1)函数指针、(2)仿函数、(3)可以转换为函数指针的函数对象、(4)类里面的成员函数以及类里面的成员变量。
在c++中lambda可以被看做是一个仿函数。operator()的类。
按照c++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。
mutable选项的作用就是在于取消operator()的const属性。
因为lambda表达式在c++中会被看作是一个仿函数,因此可以使用std::function(可调用函数包装器)进行包装称为函数指针,或者用std::bind绑定称为仿函数;但是如果没有捕获列表可以直接当一个函数指针看待。
#include <iostream>
#include <functional>
using namespace std;
void func(int x, int y) {
int a;
int b;
using ptr = void(*)(int); //定义一个函数指针。
ptr p1 = [](int x) {
cout << "x:" << x << endl;
};
p1(11);
function<void(int)> fff = [=](int x) { //function<返回值类型(参数类型)>
cout << "x:" << x << endl;
};
fff(11);
function<void(int)> ffu = bind([=](int x) { //也可以用auto进行推导类型,用auto推导的类型和function<void(int)>不是一个类型。因为auto推导的是仿函数类型,function<void(int)>是包装器对象类型。但是包装器类型可以包装auto推导出的类型
cout << "x: " << x << endl;
},placeholders::_1); //placeholders::_1是一个占位符
ffu(11);
}
int main() {
system("pause");
return 0;
}
上一篇:李超线段树
下一篇:Qt正则表达式同时匹配多个关键词