再谈一下Lambda, 以及相关注意事项; 并且推荐更多的使用lambda表达式.
最初写lambda是和function做比较, 介绍基本语法, 但是很多注意问题没有写.
前面两篇文章可以参考:
第二篇是外文的翻译
中括号问题
[]() 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
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
4id:0
id:1
id:2
final id = 4
因为生成函数对象(funcotr) f 的时候, 初始化数据成员id就是0, 并且它和外部的id没有关系(最开始是副本关系, 后续没有关系).
也就是说, 其实编译器相当于生成了一个函数对象:(对程序员来说是匿名的, 但是编译器一定给了具体的名称, 并且编译器很清楚)
1 | class Functor |
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 | class LambdaFunctor |
functor写法的框架, 规模总是存在的.但是你要是用lambda, 超级简单了, 代码如下:
1 | vector<int> v{0, 11, 12, 22, 99, 100}; |
你甚至可以直接用匿名的:
1 | v.erase(remove_if(v.begin(), v.end(), [x, y](int n) {return x < n && n<y;}), v.end()); |
并且, 注意, 直接用匿名lambda
, 好处是及时展开, 没有调用开销, 相当于inline
. (当然, 效率主要还是算法问题, 这种提升微不足道)
实际上呢, 你写的少了, 其实编译器做了更多, 尽量多用 lambda 吧.