lambda

lambda表达式:

一个lambda表达表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。
lambda 表达式:
Lambda表达式完整的声明格式如下:

[capture list] (params list) mutable exception-> return type { function body }

各项具体含义如下:
capture list:捕获外部变量列表
params list:形参列表
mutable 指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体

ps: (mutable):在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体:

#include <iostream>
using namespace std;
int main(void){
    auto f = [] { return 42; };
    auto g = [] {int a = 1; a++; return a;};
    cout << f() << endl;//42
    cout << g()  << endl;//2
    return 0;
}

注意:在 lambda 中忽略括号和参数列表等价于指定一个空参数列表。在此列表中,当调用 f 时,参数列表是空的。如果忽略返回类型,lambda 根据函数体中的代码推断出返回类型。在 c++11 标准中,如果 lambda 的函数体包含任何单一 return 语句之外的内容,且未指定返回类型,则返回 void.不过在很多编译器中好像只要有 return 语句就会返回编译器推断的类型~

向 lambda 传递参数:
#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<string> words = {"df", "fsl", "feg", "laf", "fsfl", "jfsoe"};
    stable_sort(words.begin(),words.end(),[]( string s1, string s2){return s1.size()<s2.size();});
    for(auto x:words)cout<<x<<" ";//df fsl feg laf fsfl jfsoe
    return 0;
}

其中,空捕获列表表名此 lambda 不使用它所在函数中的任何局部变量。当 stable_sort 需要比较两个元素时,它就会调用给定的这个 lambda 表达式。

注意:通常,实参和形参类型必须匹配。但与普通函数不同,lambda 不能有默认参数。因此,一个 lambda 调用的实参数目永远与形参数目相等。一旦形参初始化完毕,就可以执行函数体了。

捕获局部变量:
#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<string> words = {"df", "fsl", "feg", "laf", "fsfl", "jfsoe"};
    //lambda表达式捕获局部变量
    int sz=2;
    auto it=find_if(words.begin(),words.end(),[sz](const string &s){return s.size()>=sz;});//它在区间[first,end)中搜寻使一元判断式pred为true的第一个元素。
    cout<<*it<<endl;// df
    //对于const变量不用捕获
    const int sz1=2;
    auto it1=find_if(words.begin(),words.end(),[](const string &s){return s.size()>=sz1;});
    cout<<*it1<<endl;//df
    //同样对于static
    static int sz2=2;
    auto it2=find_if(words.begin(),words.end(),[](const string &s){return s.size()>=sz2;});
    cout<<*it2<<endl;//df

    //用于for_each
    for_each(words.begin(),words.end(),[](string s){cout<<s<<" ";});//df fsl feg laf fsfl jfsoe

    return 0;
}

值得一提的是,类似于值传递,变量捕获的方式也可以是值或者引用,和参数不同,lambda捕获的变量是在创建的时候拷贝,而不是在调用的时候拷贝。
同时:也可以[ &v ]{}方式的引用捕获,这时操作的就不是副本了,就是引用绑定的对象了。

lambda 捕获和返回:

当定义一个 lambda 时,编译器生成一个与 lambda 对应的新的(未命名的)类类型。当向一个函数传递一个 lambda 时,同时定义了一个新类型和该类型的一个对象:传递的参数就是此编译器生成的类类型的未命名对象。类似的,当使用 auto 定义一个用 lambda 初始化的变量时,定义了一个从 lambda 生成的类型的对象。

默认情况下,从 lambda 生成的类都包含一个对应 lambda 所捕获的变量的数据成员。类似任何普通类的数据成员,lambda 的数据成员也在 lambda 对象创建时就被初始化。

#include<bits/stdc++.h>
using namespace std;
void biggies(vector<string> &d, vector<string>::size_type sz,
ostream &os = cout, char c = ' '){

    for_each(d.begin(),d.end(),
                [&,c](const string&s){
                 os<<s<<c;
             });//os是隐式引用
    cout<<endl;

    for_each(d.begin(),d.end(),
             [=,&os](const string &s){os<<s<<c;});//一个隐式捕获一个显式捕获

    cout<<endl;
    int cc=1;
    for_each(d.begin(),d.end(),
        [&,cc,c](const string &s)mutable{//mutable表示可以修改捕获的值
            os<<cc++<<c<<s<<endl;
        });
    // for_each(words.begin(), words.end(),
    //     [cc, c, &](const string &s)mutable{//只能捕获列表中第一个元素可以使用隐式捕获
    //         os << cc++ << c << s << endl;
    //     });

    // for_each(words.begin(), words.end(),
    //     [=, =](const string &s)mutable{//只能有一个捕获是隐式的
    //         cout << cc++ << c << s << endl;
    //     });
}
int main(){
    int sz=3;
    vector<string> d = {"df", "fsl", "feg", "laf", "fsfl", "jfsoe"};
    auto wc=find_if(d.begin(),d.end(),
                    [=](const string&s){
                        return s.size()>=sz;
                    });//隐式捕获
    cout<<*wc<<endl;
    biggies(d,d.size(),cout);
    return 0;
}

注意:当我们混合使用隐式捕获和显示捕获时,捕获列表中的第一个元素必须是 & 或 =。此符号指定了默认捕获方式为引用或值

当混合使用隐式捕获和显示捕获时,显示捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式,则显示捕获必须采用值方式。反之亦然~

捕获列表中只能有一个元素采用隐式捕获,且该变量必须是捕获列表中的第一个元素

可变lambda:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int x=1;
//    auto get1 = [x](){ x++;return x;}//报错,如果没有mutable关键字。则一定是不可变的
    //如果希望能改变一个被捕获的变量的值,那么就可以使用mutable,
    //mutable必须在参数列表首,所以参数列表不可省略
//    auto get2 = [x]mutable{x++;return x;};//报错。不能省略参数列表
    auto get3 = [x]()mutable{x++;return x;};

    const int y=1;
//    auto get4=[y]()mutable{y++;return y;};//y是const的,不能改变。

    auto get5=[&x]{++x;return x;};
    cout<<get5()<<endl;//

//    auto get6=[&y]{++y;return y;};//y是const的,不能改变。
//    cout<<get6()<<endl;

//    auto get7=[&y]()mutable{++y;return y;};//y是const的,不能改变。加了mutable也不行
//    cout<<get7()<<endl;
    return 0;
}

注意:对于值捕获,若没加 mutalbe 关键字,则其捕获变量一定是不可变的,加了 mutable 关键字则取决于对应变量在函数中声明是否是 const 的。而引用捕获变量是否可变与 mutable 无关,只取决于绑定的对象是否是 const 的。

指定函数的返回类型
#include<bits/stdc++.h>
using namespace std;
int main(){
    array<int, 10> a = {1, -1, 2, -3, 4, -459, -94};
    /*transform函数的作用是:将某操作应用于指定范围的每个元素。transform函数有两个重载版本:
    transform(first,last,result,op);//first是容器的首迭代器,last为容器的末迭代器,result为存放结果的容器,op为要进行操作的一元函数对象或sturct、class。
    transform(first1,last1,first2,result,binary_op);//first1是第一个容器的首迭代 器,last1为第一个容器的末迭代器,first2为第二个容器的首迭代器,result为存放结果的容器,binary_op为要进行操作的二元函数 对象或sturct、class*/
    transform(a.begin(),a.end(),a.begin(),[](int x){
                return abs(x);
              });
//    for(auto x:a)cout<<x<<" ";cout<<endl;
    //符合c++11的标准的写法
    transform(a.begin(),a.end(),a.begin(),[](int x)->int{
                return -abs(x);
              });
    for(auto x:a)cout<<x<<" ";cout<<endl;
    return 0;
}
在 lambda 中不能访问 protected 成员.

bind 函数

标准库 bind 函数:

我们可以通过使用一个名为 bind 的标准库函数来解决上面提到的问题,它定义在 functional 头文件中。可以将 bind 函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来 “适应” 原对象的参数列表。调用 bind 的一般形式为:

auto newCallable = bind(callable, arg_list);

其中,newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对于给定的 calloble 的参数。即,当我们调用 newCallable 时,newCallable 会调用 callable,并传递给它 arg_list 中的参数。

arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数。这些参数是 “占位符”,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的 “位置”。数值 n 表示 生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,依此类推。

#include<bits/stdc++.h>
using namespace std;
bool isok(const string &s,int sz){
    return s.size()>sz;
}
int main(){
//    find_if只接受一元谓词,但是查找自定义条件需要两个形参,所以使用bind函数
//找到第一个长度大于5的迭代器
    int sz = 5;
    vector<string> v = {"jkfd", "jfkls", "fj", "fjslf", "fjsljfslf"};
    auto check=bind(isok,std::placeholders::_1,sz);//_1为check的第一个参数
    auto cnt=find_if(v.begin(),v.end(),check);
    cout<<*cnt;//fjsljfslf
    return 0;
}
/*
注意:此处 bind 调用只有一个占位符,表示 check6 只接受单一参数。占位符出现在 args_list 的第一个位置,表示 check6 的此参数对应 check_size 的第一个参数。此参数是一个 const string&。因此,调用 check6 必须传递给他一个 string 类型的参数,check6 会将此参数传递给 check_size
*/

_n 都定义在命名空间 placeholders 中,且此命名空间又定义在命名空间 std 中。我们也可以通过 using 来声明:

using std::placeholders::_1;

不过这种声明意味着我们对每个占位符名字都必须提供一个单独的 using 声明。我们可以使用另外一种更方便一些的声明方式:

using namespace namespace_name;

则所有来自 namespace_name 中定义的名字我们都可以直接在程序中直接使用:
(但是自己调试报错:namespace_name 不是一个命名空间)

bind 参数顺序问题:
#include<bits/stdc++.h>
using namespace std;
using namespace std::placeholders;
bool cmp(const string &s1,const string &s2){
    return s1.size()<s2.size();
}
int main(){
    array<string,5> a={"fjls", "jf", "jflsjdf", "jfslk", "jfsljflsfjsjfsl"};
    stable_sort(a.begin(),a.end(),cmp);//接收的是一个二元谓词
    for(const auto &x:a)cout<<x<<" ";cout<<endl;
    auto rcmp=bind(cmp,_2,_1);
    stable_sort(a.begin(),a.end(),rcmp);
    for(const auto &x:a)cout<<x<<" ";cout<<endl;
    return 0;
}

注意:在 stable_sort(a.begin(), a.end(), rcmp);的调用中,传入 rcmp 中的第一个参数对应 _1,第二个参数对应 _2,即是按照占位符的数字大小顺序对应的,而不是按照占位符在 rcmp 参数列表的顺序对应的。但是 rcmp 中的参数对应 cmp 中的形参列表是按照参数位置顺序一一对应的。因此,在第一个调用中,当 stable_sort 需要比较两个元素 A 和 B 时,它会调用 cmp(A, B)。在第二个 stable_sort 调用时,传递给 cmp 的参数就被交换过来了。即比较 A,B 两个元素时相当于调用的 cmp(B, A)。因此,在第一次 stable_sort 调用的结果是对 a 中字符串按长度升序排列,而第二次调用 stable_sort 调用的结果是对 a 中的字符串按长度降序排列~

绑定引用参数:
#include<bits/stdc++.h>
using namespace std;
using namespace std::placeholders;
ostream &print(ostream &os, const string &s, char c){
    os << s << c;
}
int main(){
    vector<string> v = {"fjs", "fjsl", "fjslf", "fjlsjf"};
    ostream &os=std::cout;
    char c=' ';
    for_each(v.begin(),v.end(),
             [&os,c](const string &s){
                os<<s<<c;
             });

   //以下绑定引用参数的值来实现
   //for_each(v.begin(),v.end(),bind(print,os,c,_1));//os是默认值传递的,
    for_each(v.begin(),v.end(),bind(print,ref(os),_1,c));
    //ref函数:返回一个对象的应用,传入一个对象。
}

注意:对于不能拷贝的对象,需要通过 ref 函数才能用作 bind 的非占位符参数

函数 ref() 返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有一个 cref 函数,生成一个保存 const 引用的类。ref 和 cref 定义在头文件 functional 中。

借鉴自:https://www.cnblogs.com/geloutingyu/p/8335230.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值