技术: C++11 Lambda 再探究

再谈一下Lambda, 以及相关注意事项; 并且推荐更多的使用lambda表达式.

最初写lambda是和function做比较, 介绍基本语法, 但是很多注意问题没有写.

前面两篇文章可以参考:

  1. lambda in cpp
  2. bind和lambda性能比较

第二篇是外文的翻译

post-cover

中括号问题

[]() mutable throw -> returnType {};

中括号和圆括号的区别, 上一次基本已经说清楚了:

圆括号和函数行参没有太多区别; 方括号就是让函数体外的变量对函数体可见, 是scope aware.

但是这样还没有解释清楚, 从编译器的角度来说, 上面的从scope上的解释还不够确切.

当把 lambda 和 functor 进行对比是就能发现奥妙了.

凡是在[]号里面主动传入的, 都将作为匿名仿函数对象的数据成员

例如下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
int id = 0;
auto f = [id]()mutable{
std::cout << "id:" << id << std::endl;
++id; //ok
};

id =4;
f();
f();
f();

std::cout << "final id = " << id << std::endl;

return 0;
}

值传递, 并且可以改变; 但是输出结果是:

1
2
3
4
id:0
id:1
id:2
final id = 4

因为生成函数对象(funcotr) f 的时候, 初始化数据成员id就是0, 并且它和外部的id没有关系(最开始是副本关系, 后续没有关系).
也就是说, 其实编译器相当于生成了一个函数对象:(对程序员来说是匿名的, 但是编译器一定给了具体的名称, 并且编译器很清楚)

1
2
3
4
5
6
7
8
9
10
class Functor
{
private:
int id; //外部id传入时的拷贝
public:
void operator()(){
std::cout << "id:" << id << std::endl;
++id;
}
};

mutable问题

如果没有mutable关键字, 即便 pass by value, 也不能在lambda表达式内部进行修改值操作.
换句话说 mutable 就是用来修饰中括号里面可见的外部变量的(值传递的情况).

总结: (中括号里面特殊一些)

中括号里面 pass by value, 那么要修改变量, 一定要mutable修饰; (否则编译就报错了)
中括号里面 pass by reference 时, 内部可以直接修改, 不需要mutable修饰.(gnu g++下验证通过)

小括号pass by value则不需要, 小括号随意(因为小括号就相当于函数的形参, 而不是超出作用域的捕捉变量)

尽量用lambda代替functor

个人觉得lambda最大的好处, 就是代替functor, 实现 in place 的方便, 随时需要, 随时写;

如果你写函数对象, 那么你至少要实现构造器, 赋值函数, 以及operator()(), 甚至还要指定函数对象的数据成员,
比如说我要

删除vector里面10-100的数据(前闭后开)

那么functor的成员就应该保留10和100, 大致上是这个样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class LambdaFunctor
{
public:
LambdaFunctor(int a, int b): m_a(a), m_b(b) {}
bool operator()(int n) const {
return m_a < n && n<m_b; //参数n来自容器元素
}
private:
int m_a;
int m_b;
};

//调用逻辑
vector<int> v{0, 11, 12, 22, 99, 100};
int x=10, y=100;
v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x,y)), v.end());

functor写法的框架, 规模总是存在的.但是你要是用lambda, 超级简单了, 代码如下:

1
2
3
4
5
vector<int> v{0, 11, 12, 22, 99, 100};
int x=10, y=100;

auto f = [x, y](int n) {return x < n && n<y;};
v.erase(remove_if(v.begin(), v.end(), f), v.end());

你甚至可以直接用匿名的:

1
v.erase(remove_if(v.begin(), v.end(), [x, y](int n) {return x < n && n<y;}), v.end());

并且, 注意, 直接用匿名lambda, 好处是及时展开, 没有调用开销, 相当于inline. (当然, 效率主要还是算法问题, 这种提升微不足道)

实际上呢, 你写的少了, 其实编译器做了更多, 尽量多用 lambda 吧.

文章目录
  1. 1. 中括号问题
  2. 2. mutable问题
  3. 3. 尽量用lambda代替functor
|