C++2.0新特性之一lambda,本节学习激动人心的lambda.
[capture list] (params list) mutable exception-> return type { function body }
简言之 [ 捕获 ] ( 形参 ) 可选参数 -> 返回类型 { 函数体 }
匿名无参函数
例如:
[] {
cout << "hello" << endl;
}();
同样我们可以使用auto关键字来获得lambda的返回类型:
auto I = [] {
cout << "hello" << endl;
};
I();
对于lambda的返回,一般人不知道其返回类型是什么,所以常常使用auto,这也是auto使用比较多的一点,像这个比较简单,我们可以直接写出返回类型:
funciton<void()> I = [] {
cout << "hello" << endl;
};
I();
捕获列表
引入上述例子看一下[]
可以做哪些事: 常常也被称为引入符号(lambda-introduct).
我们来演示捕获作用:
传值
int id=2,id1=3;
auto f = [=]() { // 这里加不加()都可以
cout << "id=" << id << endl;
cout << "id1=" << id1 << endl;
};
id=3;
f();
输出:
id=2
id1=3
这里主要来说明前面id被修改为3,为何输出没变;另外lambda可以访问到外部所有变量;里面的值如何进行修改呢?
第一个问题:输出不变?
因为是传值,外部修改对f函数没影响,所以id仍旧是2,要想修改看后面引用传入.
第二个问题:访问到所有变量?
因为使用了=,=表示默认以by value传递外部所有变量,也就是前面的捕获列表为=.
第三个问题:如何修改外部变量? 可以通过添加mutable,但是当lambad函数结束后,变量仍旧恢复. 例如:
int id=2,id1=3;
auto f = [=]()mutable{
id--;
cout << "id=" << id << endl; // 1
cout << "id1=" << id1 << endl; // 3
};
id=3;
f();
内部可以对外部变量修改,但是跳出lambda,并不会影响外部,如果要影响,必须传引用.
另外,如果lambda内部修改了变量,但是并没有使用mutable关键字,报错:error: increment of read-only variable ‘id’.且使用mutable后,()必须添加()。
传引用
int id=2,id1=3;
auto f = [&](){
id++;
cout << "id=" << id << endl; // 4
cout << "id1=" << id1 << endl; // 3
};
id=3;
f();
cout << "id=" << id << endl; // 4
看上面输出,我们知道id外部修改,对内部发生了影响,且内部修改也对外部进行了修改,这就是传引用!传递引用可以不加mutable关键字.
其余的一部分传引用,其余传值或者一部分传值,其余传引用,只需要记着,传值必须用mutable关键字进行修改,且对外部变量没有影响.传引用会影响即可.
参数列表
前面都是对lambda函数不传递任何参数,传递参数就比较简单了,调用的时候:
f(xxx);
即可. 那,我们写一个简单的:
function<int(int x)> ll = [](int x) -> int { // 由于这个返回值比较简单,可以不用显示写->int 返回类型,例如当想转为float类型等操作的时候,可以显示写出->float.
return x + 10;
};
对传递进来的值加10操作.
返回类型
还是上述例子,通过-> return type`来使用.
this指针
对于[=]与[&],可以直接使用this指针,而对于[]
则需要显示[this]
.
例如:
class X {
private:
int __x, __y;
public:
X(int x, int y) : __x(x), __y(y) {}
int operator()(int a) { return a; }
int f() {
// 下列 lambda 的语境是成员函数 X::f
// 对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针
return [&]() -> int {
return operator()(this->__x + __y); // X::operator()(this->x + (*this).y)
// 拥有类型 X*
}();
}
int ff() {
return [this]() {
return this->__x;
}();
}
};
调用:
// this 指针
X x_(1, 2);
cout << "x_.f()=" << x_.f() << endl; // 1+2=3
cout << "x_.ff()=" << x_.ff() << endl; // 1
lambda等价操作
我们可以把lambda的使用等价于下面这个类,通过这两个来做对比:
class UnNamedLocalFunction {
private:
int localVar;
public:
UnNamedLocalFunction(int var) : localVar(var) {}
bool operator()(int val) {
return val == localVar;
}
};
调用:
UnNamedLocalFunction lambda2(tobefound);
bool b2 = lambda2(5);
lambda使用:
int tobefound = 5;
auto lambda1 = [tobefound](int val) {
return val == tobefound;
};
bool b1 = lambda1(5);
decltype+lambda
在使用STL容器的时候,通常需要比大小,例如set,需要传递一个比较器,针对这个我们可以使用lambda来传递.
例如:我们有个Person类,我们比大小是根据这个人的lastname来比较
struct Person {
string firstname;
string lastname;
};
写比较器: 由于lambda的返回类型很少友人知道,书写比较困难,因此我们可以直接用auto.
auto cmp = [](const Person &p1, const Person &p2) { // 如果对lambda比较熟悉,可以直接写返回类型:function<bool(const Person&p1,const Person&p2)>
return p1.lastname < p2.lastname;
};
我们使用set来去重当前容器中的Person是否重复,这里就需要传递一个比较器:
set<Person, decltype(cmp)> col(cmp);
这里使用decltype(cmp)来获取返回类型,如果功力深厚,可以直接写:
set<Person, function<bool(const Person&p1,const Person&p2)> > col(cmp);
我们通常使用decltype推断类型+lambda来书写这种比较复杂的例子.
算法+lambda
在容器的算法中常常使用lambda,例如remove_if
,for_each
等等.
例如:我们实现一个移除vector中30到100之间的所有数字,只需要后面三行:
vector<int> vec{5, 28, 50, 83, 70, 590, 245, 59, 24};
int x = 30, y = 100;
vec.erase(remove_if(vec.begin(), vec.end(), [x, y](int n) { return x < n && n < y; }), vec.end());
for_each(vec.begin(), vec.end(), [](int i) { cout << i << " "; });
cout << endl;
而如果不用lambda,则需要写这么多:
public:
LambdaFunctor(int a, int b) : m_a(a), m_b(b) {}
bool operator()(int n) const {
return m_a < n && n < m_b;
}
private:
int m_a;
int m_b;
};
vec.erase(remove_if(vec.begin(), vec.end(), LambdaFunctor(x, y)), vec.end());
for(auto i:vec) cout<<i<<" ";
cout<<endl;
最后,c++14,17,20都对lambda做了一些修改,本节只是比较简单的学习了c++2.0的新特性,期待大家共同交流!