前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++也能像Python一样玩转lambda

C++也能像Python一样玩转lambda

作者头像
公众号guangcity
发布2019-11-06 11:48:57
4200
发布2019-11-06 11:48:57
举报
文章被收录于专栏:光城(guangcity)

C++也能像Python一样玩转lambda

0.导语

C++2.0新特性之一lambda,本节学习激动人心的lambda.

1.lambda表达式

代码语言:javascript
复制
[capture list] (params list) mutable exception-> return type { function body }
  • capture list:捕获外部变量列表
  • params list:形参列表
  • mutable指示符:用来说用是否可以修改捕获的变量
  • exception:异常设定
  • return type:返回类型
  • function body:函数体

简言之 [ 捕获 ] ( 形参 ) 可选参数 -> 返回类型 { 函数体 }

2.从简单到复杂

匿名无参函数

例如:

代码语言:javascript
复制
[] {
    cout << "hello" << endl;
}();

同样我们可以使用auto关键字来获得lambda的返回类型:

代码语言:javascript
复制
auto I = [] {
    cout << "hello" << endl;
};
I();

对于lambda的返回,一般人不知道其返回类型是什么,所以常常使用auto,这也是auto使用比较多的一点,像这个比较简单,我们可以直接写出返回类型:

代码语言:javascript
复制
funciton<void()> I = [] {
    cout << "hello" << endl;
};
I();

捕获列表

引入上述例子看一下[]可以做哪些事: 常常也被称为引入符号(lambda-introduct).

  • 声明当前是个lambda表达式
  • 捕获列表

我们来演示捕获作用:

  • [=] =表示默认以by value传递外部所有变量
  • [&] &表示默认以by reference传递外部所有变量
  • [=,&id,...] 一部分传引用,其余传值
  • [&,id,...] 一部分传值,其余传引用

传值

代码语言:javascript
复制
int id=2,id1=3;
auto f = [=]() {       // 这里加不加()都可以
    cout << "id=" << id << endl;
    cout << "id1=" << id1 << endl;
};
id=3;
f();

输出:

代码语言:javascript
复制
id=2
id1=3

这里主要来说明前面id被修改为3,为何输出没变;另外lambda可以访问到外部所有变量;里面的值如何进行修改呢?

第一个问题:输出不变?

因为是传值,外部修改对f函数没影响,所以id仍旧是2,要想修改看后面引用传入.

第二个问题:访问到所有变量?

因为使用了=,=表示默认以by value传递外部所有变量,也就是前面的捕获列表为=.

第三个问题:如何修改外部变量? 可以通过添加mutable,但是当lambad函数结束后,变量仍旧恢复. 例如:

代码语言:javascript
复制
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后,()必须添加()

传引用

代码语言:javascript
复制
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函数不传递任何参数,传递参数就比较简单了,调用的时候:

代码语言:javascript
复制
f(xxx);

即可. 那,我们写一个简单的:

代码语言:javascript
复制
function<int(int x)> ll = [](int x) -> int {    // 由于这个返回值比较简单,可以不用显示写->int 返回类型,例如当想转为float类型等操作的时候,可以显示写出->float.
    return x + 10;
};

对传递进来的值加10操作.

返回类型

还是上述例子,通过-> return type`来使用.

this指针

对于[=]与[&],可以直接使用this指针,而对于[]则需要显示[this].

例如:

代码语言:javascript
复制
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;
        }();
    }
};

调用:

代码语言:javascript
复制
// this 指针
X x_(1, 2);
cout << "x_.f()=" << x_.f() << endl;   // 1+2=3
cout << "x_.ff()=" << x_.ff() << endl; // 1

lambda等价操作

我们可以把lambda的使用等价于下面这个类,通过这两个来做对比:

代码语言:javascript
复制
class UnNamedLocalFunction {
private:
    int localVar;
public:
    UnNamedLocalFunction(int var) : localVar(var) {}

    bool operator()(int val) {
        return val == localVar;
    }
};

调用:

代码语言:javascript
复制
UnNamedLocalFunction lambda2(tobefound);
bool b2 = lambda2(5);

lambda使用:

代码语言:javascript
复制
int tobefound = 5;
auto lambda1 = [tobefound](int val) {
    return val == tobefound;
};
bool b1 = lambda1(5);

decltype+lambda

在使用STL容器的时候,通常需要比大小,例如set,需要传递一个比较器,针对这个我们可以使用lambda来传递.

例如:我们有个Person类,我们比大小是根据这个人的lastname来比较

代码语言:javascript
复制
struct Person {
    string firstname;
    string lastname;
};

写比较器: 由于lambda的返回类型很少友人知道,书写比较困难,因此我们可以直接用auto.

代码语言:javascript
复制
auto cmp = [](const Person &p1, const Person &p2) {  // 如果对lambda比较熟悉,可以直接写返回类型:function<bool(const Person&p1,const Person&p2)>
    return p1.lastname < p2.lastname;
};

我们使用set来去重当前容器中的Person是否重复,这里就需要传递一个比较器:

代码语言:javascript
复制
set<Person, decltype(cmp)> col(cmp);

这里使用decltype(cmp)来获取返回类型,如果功力深厚,可以直接写:

代码语言:javascript
复制
set<Person, function<bool(const Person&p1,const Person&p2)> > col(cmp);

我们通常使用decltype推断类型+lambda来书写这种比较复杂的例子.

算法+lambda

在容器的算法中常常使用lambda,例如remove_if,for_each等等.

例如:我们实现一个移除vector中30到100之间的所有数字,只需要后面三行:

代码语言:javascript
复制
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,则需要写这么多:

代码语言:javascript
复制
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的新特性,期待大家共同交流!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++也能像Python一样玩转lambda
    • 0.导语
      • 1.lambda表达式
        • 2.从简单到复杂
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档