前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c/c++补完计划(六): 语法糖lambda

c/c++补完计划(六): 语法糖lambda

作者头像
sean_yang
发布2020-08-11 11:17:34
5580
发布2020-08-11 11:17:34
举报
文章被收录于专栏:Sorrower的专栏

前言

很多语言都有lambda, c++自然不能缺, 在c++11里面加入了, 是程序猿喜欢的语法糖, 便于阅读, 也便于理解. 当然, 它有很多相关概念, 这里尽可能展开说.

lambda表达式

代码语言:javascript
复制
[capture list] (params list) mutable exception-> return type { function body }
  • 捕获子句(在c++规范中也称为lambda引导)
  • 参数列表可有可无. (也称为lambda 声明符)
  • 可变规范可有可无.
  • 异常规范可有可无.
  • 尾随-返回类型可有可无.
  • lambda体, 也就是函数体.

你会觉得和函数声明很像, 但是注意, 不可以有默认值, 也不支持可变参数, 类似printf, 参数必须要有参数名. 甚至你会觉得和python3很像, 尤其是这个->return type, 所以你看, c++也越来越友好了.

先来几个小栗子:

代码语言:javascript
复制
auto f = [](int a, int b) { return a + b; };
cout << f(1, 5) << endl;

可以看到a, b是形参, 1, 5是实参, f似乎是函数, 这个放到后面说.

代码语言:javascript
复制
int x = 3, y = 5;
auto f2 = [x, y] { return x + y; };
cout << f2() << endl;

然后你发现, lambda更神奇的地方在于, 可以捕获外界参数, 是不是很酷, 其实这里本质还是值传递, 当然后面有引用, 但是默认是值传递.

参数捕获

捕获形式

说明

[]

不捕获任何外部变量

[var1,var2, …]

默认以值得形式捕获指定的多个外部变量(用逗号分隔)

[this]

以值的形式捕获this指针

[=]

以值的形式捕获所有外部变量

[&]

以引用形式捕获所有外部变量

[=, &var1]

变量var1以引用形式捕获,其余变量以传值形式捕获

[&, var1]

变量var1以值的形式捕获,其余变量以引用形式捕获

很多都是一眼懂, 举个栗子吧:

代码语言:javascript
复制
int x = 1, y = 2, z = 3;
cout << "before: " << x << ' ' << y << ' ' << z << endl;
auto f = [&, x]()mutable {
    x = 5;
    y = 6;
    z = 7;
    cout << "in: " << x << ' ' << y << ' ' << z << endl;
};
f();
cout << "after: " << x << ' ' << y << ' ' << z << endl;
代码语言:javascript
复制
before: 1 2 3
in: 5 6 7
after: 1 6 7

x是值传递, y, z是引用传递, 值传递实际上是不能修改的, 但是这里加了mutable, 所以可以在函数体内进行改动. 然后引用传入, 会对外部产生影响, 值传入则不会, 很好理解.

this

甚至可以传入this指针.

代码语言:javascript
复制
class A {
public:
    explicit A(int d) : data(d) {}

    void foo(vector<int> &v) {
        for_each(v.begin(), v.end(), [this](int i) {
            cout << "before: " << i << endl;
            i += data;
            cout << "after: " << i << endl;
        });
    }

private:
    int data;
};

vector<int> v{1, 2, 3};
A a(3);
a.foo(v);
代码语言:javascript
复制
before: 1
after: 4
before: 2
after: 5
before: 3
after: 6

传入this指针之后, 就可以使用成员变量.

function

然后你会发现一个问题, 我这里写的都是auto, 那具体是什么呢?

代码语言:javascript
复制
function<int(int, int)> f = [](int a, int b) { return a + b; };

返回一开始的栗子, 看到function里面写了函数的返回值, 然后括号里面是参数类型. std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。 目的肯定是取代函数指针的, 来看一个栗子:

代码语言:javascript
复制
function<int(int, int)> add = [](int a, int b) { return a + b; };
cout << add(1, 5) << endl;
function<int(int, int)> div = [](int a, int b) { return a / b; };
cout << div(5, 1) << endl;

想想之前的写法, 是不是用宏定义一个函数指针, 类似typedef int(*fp)(int, int);, 是不是很像, 用function的话, 整体也更直观好操作.

bind

曾经我被问到, 如何实现lambda获取外部参数的功能, 那答案就是利用bind, 先来看个栗子:

代码语言:javascript
复制
function<int(int, int)> div = [](int a, int b) { return a / b; };
cout << div(5, 1) << endl;
auto divEx = bind(div, placeholders::_2, placeholders::_1);
cout << divEx(5, 1) << endl;
代码语言:javascript
复制
5
0

是不是挺酷的, 什么都没改, 就把参数位置换了.

代码语言:javascript
复制
A a;
auto ret = bind(&A::f, &a, 5, placeholders::_1);
cout << ret(3) << endl;

之前也说了, lambda是不支持默认参数的, 但是通过bind+占位符, 就很好实现了这个点, 甚至可以随意调整参数位置. 而且可以绑定成员函数, 不过你需要传入具体实例在第二个参数.

代码语言:javascript
复制
void print(ostream &os, string &str, char space) {
    os << str << space;
}

ostringstream os;
vector<string> data{"cpp", "c", "java"};
char space = ' ';
for_each(data.begin(), data.end(), bind(print, ref(os), placeholders::_1, space));
cout << os.str() << endl;

os.str("");
for_each(data.begin(), data.end(), [&os, space](string &str){os<< str << space;});
cout << os.str() << endl;

os.str("");
auto printL=[&os, space](string&str){os<<str<<space;};
for_each(data.begin(), data.end(), printL);
cout << os.str() << endl;

os.str("");
auto printL=[](ostream &os, string&str, char space){os<<str<<space;};
for_each(data.begin(), data.end(), bind (printL, ref(os), placeholders::_1, space));
cout << os.str() << endl;

最后对比下bind和lambda, 当然二者是可以配合使用的, 怎么用还是看自己. 我的话, 怎么简洁怎么用. ref简单说下, 主要是有些变量不支持值传递, bind这里就要写成ref(os).

最后

真的是很实用的点, 让c++灵活了很多. 就像设计了while之后, 又设计了for, 设计了for之后又设计了for_each, 语言都是不断发展的, 学习新的真的很有必要.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • lambda表达式
    • 参数捕获
      • this
      • function
      • bind
      • 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档