C++11 Lambda表达式和bind

lambda及参数绑定

一、介绍

  对于STL中的算法,我们都可以传递任何类别的可调用对象。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。即,如果e是一个可调用的表达式,则我们可以编写代码e(args),其中args是一个逗号分隔的一个或多个参数的列表。

  一般来说,有四种可调用对象:函数,函数指针,重载了函数调用运算符的类,以及lambda表达式。

二、lambda表达式

1. 概述

  一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部。

  一个lambda的通用表达式为:

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

举个简单的例子:

auto f = [] { return 42; };
cout << f() << endl; //打印42

注意,忽略括号和参数列表相当于指定了空的参数列表。如果函数体为一条return语句,lambda将根据代码推断返回类型,否则类型为void。

2. 传递参数

  如函数一样,lambda表达式也可以传递参数,但是不同的是,其不能有默认参数。一个lambda调用的实参数目永远要和形参数目相等。

  举个例子:

auto f = [](const string &lhs, const string &rhs) 
    { return a.size() < b.size(); };

3. 使用捕获列表

  所谓捕获列表,则是中括号中的参数,表示使用其所在函数中的任何局部变量。捕获的方式有三种:值捕获引用捕获隐式捕获

三、Lambda捕获列表

1、空,没有使用任何函数对象参数。

2、=,函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

3、&,函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量),不过要注意是const引用。

    size_t v1 = 42;
    auto f    = [&]() {//捕捉所有代码块中对象并以引用方式捕捉
        cout << "Lambda " << v1 << endl;
        v1 = 1000;//修改引用所绑定对象的值
        return v1 + 10;
    };
    
//    v1     = 0;
    auto j = f();
    cout << "end:" << j << endl;
    cout << v1 << endl;    

   捕捉列表按引用传递可以修改传递对象的值,v1的值成了1000

4、this,函数体内可以使用Lambda所在类中的成员变量。同理this的含义,按*this传递,可修改类成员age的值,变成了10000

class Person {
public:
    void touch() {
        auto f = [this]() {
            int cout  = this->age;
            this->age = 10000;
            return ++cout;
        };
        auto j = f();
    }
    
    int age = 10;
    int sex = 1;
};

5、a,将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

6、&a,将a按引用进行传递。

7、a, &b,将a按值进行传递,b按引用进行传递。

8、=,&a, &b,除a和b按引用进行传递外,其他参数都按值进行传递。

9、&, a, b,除a和b按值进行传递外,其他参数都按引用进行传递。

四、 指定返回类型

  如前面所提到,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。

 
//错误示范:编译器推断为void,实际为int
auto f = [](int i) { if(i < 0) return -i; else return i; };
 
//可以修改为如下
auto f = [](int i) -> int { if(i < 0) return -i; else return i; };

五、关于bind的用法:

   1.可将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

   调用bind的一般形式为:

auto newCallable = bind(callable,arg_list);

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

占位符类型作用域

using namespace std::placeholders;

例子:

int f(int a, int b, int c) {
    cout << "a " << a << " b " << b << " c " << c << endl;
    return a - b + c;
}
//调用处
auto g = bind(f, _2, _1, 11);//注意:此处参数顺序.._2,_1),但是bind函数参数顺序是(20,10)
int k  = g(10, 20);
cout << k << endl;  //输出21

实际应用中:

bool check_size(const int x, int sz) {
    return x > sz;
}
//调用处
int n = 5;
    //有bind函数新封装生成的函数,其内部调用了check_size
auto new_check_size = bind(check_size, std::placeholders::_1, n);
    
auto it = find_if(v.begin(), v.end(), new_check_size);
    
cout << "第一个大于n的数为:" << *it << endl;

例如cocos中也是类似实现方式:

进入CC_CALLBACK_1实现:

为了与不支持拷贝的参数绑定,bind经常和ref一起用,ref也定义在头文件functional中,作用是返回一个引用对象。

 
ostream &print(ostream &os, const string &s, char c) {
    return os << s << c;
}
//错误示范
for_each(words.begin(), words.end(), bind(print, os, _1, ' '));
//正确示范
for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值