C++lambda表达式及bind函数总结

目录

引入lambda

值捕获

使用捕获列表

参数绑定

标准库bind函数

绑定check_size的sz参数

编程例子


以下是对阅读<>lambda阅读部分总结

在实际的编程过程中,对于某些函数,我们希望对于传入的参数列表,其中某些参数是固定的值,而另外一个参数随着调用次数的增长而传入一个像STL容器一样的可迭代对象,对象的值随着*iterator值得不同而改变,每次调用该函数后会执行iterator++,并再次通过相同的传入参数调用该函数。

引入lambda

值捕获

我们可以向一个算法传递任意类别的可迭代对象。对于一个对象或者一个表达式,如果可以对其使用调用运算符,则称它为可调用的。一个lambda表达一个可调用单元,可以将lambda理解为一个内联函数,他直接将返回值内嵌在其它函数当中。与任何函数类似,一个lambda具有一个返回类型、一个参数列表、一个函数体,它的基本形式为:

[capture list](param list)->return type{function body}

lambda捕获可分为值捕获及引用捕获,值捕获参数是传入参数的拷贝,因此在函数执行是如果修改了外部传入参数的值,并不影响lambda的返回值:

void fcn1()
{
    size_t v1=42;//局部变量
    //将v1拷贝到名为f的可调用对象
    auto f=[v1] {return v1;};
    v1=0;
    auto j=f();//j为42,f()保存了我们创建它时v1的拷贝
}

另一种捕获类型为引用捕获,与值引用相反,引用捕获直接修改内存内的数据:

void fcn2()
{
    size_t v1=42;//局部变量
    //将v1拷贝到名为f的可调用对象
    auto f2=[&v1] {return v1;};
    v1=0;
    auto j=f2();//j为0,f2()保存了v1的引用而非拷贝
}

捕获引用有事能帮助我们进行一些常规的ostream操作来帮助我们格式化输出字符数据。

void biggies(vector &words,vector::size_type,ostream &os=cout,char c=' ')
{
    //os隐式捕获,引用捕获方式;c隐式捕获,值捕获方式
    for_each(words.begin(),words.end(),[&,c](const string &s){os<

使用捕获列表

lambda可以出现在一个函数中,使用其局部变量,但它只能使用那些明确指明的变量,捕获列表指引lambda在其内部包含访问局部变量所需的信息。在lambda函数中出现的变量必须由调用它的函数提供,但必须要在参数列表声明,如:

[sz](const string &a)
{
    return a.size()>sz
}//正确

[](const string &a)
{
    return a.size()>sz
}//错误,sz未捕获

参数绑定

如果lambda的捕获列表为空通常可以用函数来代替它。但是对于捕获局部变量的lambda就不是这么容易了。例如,我们用在find_if调用中的lambda比较一个string和一个给定大小。我们可以很容易地编写一个完成同样功能的函数,find_if定义在标准库头文件,它返回第一个满足一元谓词元素的迭代器,由于返回的是一个迭代器,要获取它的实际值,需要在返回迭代器前面加上运算符*:

bool check_size(const string &s,string::size_type sz)
{
    return s.size() >=sz;
}

但是,我们不能用这个函数作为find_if的一个参数。因为find_if接受一个一元谓词,因此传递给find_if的可调用对象必须接受单一参数。

标准库bind函数

我们可以解决像check_size传递一个长度参数的问题,方法是一个新的名为bind的标准库参数,它定义在头文件functional中,可将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个可调用对象来"适应"原对象的参数列表,调用bind的一般形式为:

auto newCallable=bind(callable,arg_list)

绑定check_size的sz参数

我们可以使用bind生成一个调用check_size的对象,如下,它用一个定制作为其大小参数来调用check_size

//check6是一个可调用对象,接受一个string类型的参数,并用此string和值6来调用check_size
auto check_size=bind(check_size,_1,6);

此bind调用只有一个占位符来表示check只接受单一参数,占位符出现在arg_list的第一个未知,表示check6的此参数对应于前面所述的参数绑定下的check_size函数中的第一个参数,而第二个参数则传入值6。

需要注意的是名字_n定义在命名空间placeholders中,需要使用using namespace std::placeholders来预先声明。

string s="hello"
bool b1=check6(s)//check6(s)会调用check_size(s,6)

通过查看b1是否为true来判断字符串"hello"的长度是否>6。另外使用bind,我们可以将原来基于lambda的find_if调用

auto wc=find_if(word.begin(),words.end,[sz](Const(string &a)));

替换为如下版本:

auto wc=find_if(words.begin(),words.end(),bind(check_size,-1,sz));

利用标准库提供的适配器,我们可以实现一些不常用但功能简单的函数:

统计大于1024的值有多少个

找到第一个不等于pooh的字符串

将所有值都乘以2

定义的函数为:

std::count_if(ivec.cbegin(), ivec.cend(), std::bind(std::greater(), _1, 1024));
std::find_if(svec.cbegin(), svec.cend(), std::bind(std::not_equal_to(), _1, "pooh"));
std::transform(ivec.begin(), ivec.end(), ivec.begin(), std::bind(std::multiplies(), _1, 2));

greater、not_equal_to、mutiplies都是定义在头文件下的标准库函数对象。

编程例子

废话不多说,通过编程例子体会一下lambda函数和bind函数有什么不同吧。

#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace::placeholders;


int main()
{  
    bool checkSize(const string &s,string::size_type sz);//外部函数声明
    auto check6 =bind(checkSize,_1,6);
    string s="hello";
    vector words;
    int sz=6;
    words.push_back("fox");
    words.push_back("jump");
    words.push_back("over");
    words.push_back("quick");
    words.push_back("red");
    words.push_back("red");
    words.push_back("slow");
    words.push_back("the");
    words.push_back("faiofjsfs");
    words.push_back("turtle");
    bool b1=check6(s);
    cout<sz;});
    cout<<"find the element of words of which size > 6 based on bind,the result is"<<*wc< 6 based on lambda,the result is "<<*wc2<=sz;      
}

 

你可能感兴趣的:(C++,c++)