C++新特性(三)

lambda表达式

1. Lambda表达式基础

lambda 表达式是c++11最重要的新特性之一,lambda表达式实际上就是提供了一个类似匿名函数的特性,而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下使用的。这样的场景有很多。所以匿名函数几乎是现代编程语言的标配。
应用场景
Qt中的槽函数,槽函数通常只在连接信号槽的地方使用,没有必要命名一个函数。
基本语法

[捕获列表](参数列表) mutable(可选) 异常属性 ->返回值类型{
	//函数体
}
//例如:
[](int a, int b) -> int{
	return a+b;
}
  • Lambda表达式以一对中括号[]开始。
  • 和函数定义一样,有参数列表()
  • 和函数定义一样,有函数体{}。
  • 一般不需要说明返回值->(相当于auto),有特殊情况需要说明时,则应使用箭头语法的方式。
  • 每个Lambda表达式都有一个全局唯一的类型,要精确捕获lambda表达式到一个变量中,只能通过auto声明的方式。

2. 用法

2.1 可以嵌套调用

//1. 直接调用
int c = [](int n){
	return [n](int x){
		return n+x;
	}(1);//内部的lambda表达式捕获了外部的参数n,并传参参数1.
}(2);// 外部的lambda表达式调用内部的lambda表达式并传参2.
//结果返回3.

//2.保存到一个变量中,在适当的地方再调用。
auto f = [](int n){
	return [n](int x){
		return n+x;
	};//先不传参数, 不调用.
};// 先不传参数, 不调用.
int c = f(1)(2);//分别传两个参数。效果和上面一样。

2.2 捕获列表

捕获列表可以理解为参数的一种类型,lambda表达式函数体内部默认是不能使用外部的变量,捕获列表可以起到传递外部参数的作用,根据传递的行为, 捕获列表分为以下几种:

1.[var]表示值传递方式捕捉变量var;
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var;
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this);
5.[this]表示值传递方式捕捉当前的this指针。
6.[=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
7.[&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。

值得注意的是,捕捉列表不允许变量重复传递,会导致编译时期的错误.

2.2.1按值捕获:

与参数传值类似,是一份拷贝,拷贝的是匿名函数定义时(非调用时)外部变量的值。在匿名函数内部修改该变量不会影响外面的值,同样,在匿名函数定义体之后修改该变量的值,也不会影响匿名函数内部该变量的值。被捕获的变量在lambda表达式被创建时拷贝,而非调用时才拷贝。

int main()
{
	int t = 10;
	//按值捕获,捕获的是声明匿名函数时,捕获列表参数的值。
	auto f = [t](){
		cout<<t<<endl;
	}
	t = 11;
	f(); //此时打印出t的值是10, 而不是11.
}

同一个匿名函数多次被调用时,会维持上一个值(相当于c中的static变量),匿名函数退出时,值捕获/引用捕获的参数不释放:

int main()
{
	int t = 10;
	//按值捕获,捕获的是声明匿名函数时,捕获列表参数的值。
	auto f = [t]()mutable{
		++t;
	}
	cout<<f()<<endl;//输出为11,在定义匿名函数的时候,t的值就在内部拷贝了一份,并复制10。
	cout<<f()<<endl;//输出为12,第二次进来的时候,t的值维持之前的拷贝。
	cout<<t<<endl;//输出为10,内部不影响外部。
}

mutable关键字
mutalbe的中文意思是“可变的,易变的”,跟const是反义词。默认情况下按值捕获的参数是const类型的,不可修改。如果要修改t的值,要加上mutable关键字。

2.2.2按引用捕获:

与引用传参类似,引用捕获传的是外部变量的引用,用的是同一份资源,在匿名函数内部改变该变量会影响外部的值,在匿名函数外部改变该变量的值也会影响内部的值。

int main()
{
	int t = 10;
	//按引用捕获,捕获列表参数的引用,内部修改会影响外部,外部修改会影响内部。
	auto f = [&t](){
		cout<<t<<endl;
		t = 13;
	}
	t = 11;
	f(); //此时打印出t的值是11,因为t在匿名函数外部被修改。
	cout<<t<<endl;//此时打印出t的值是13,因为t在匿名函数被修改.
}

同一个匿名函数多次被调用时,会维持上一个值(相当于C/C++中的static变量),匿名函数退出时,值捕获/引用捕获的参数不释放:

int main()
{
    int t = 10;
    //按引用捕获,捕获的是调用匿名函数时,捕获列表参数的值。
    auto f = [&t]()mutable{
       return  ++t;
    };
    t=12;
	cout<<f()<<endl;//输出为13
	cout<<f()<<endl;//输出为14
	cout<<t<<endl;//输出为14
}

3场景示例

int main()
{

    //遍历vector,并分别打印出奇数和偶数。
    vector<int> v = {1,2,3,4,5,6};
    for_each(v.begin(),v.end(),[=](int n){
        if(n%2)
        {
            cout <<"this is odd"<<endl;
        }
        else
        {
            cout <<"this is even"<<endl;
        }

4 函数对象包装器

为函数提供了一种容器(封装)

4.1 std::function

Lambda表达式的本质是一个函数对象,当lambda表达式的捕获列表为空时,lambda表达式还能够作为一个函数指针进行传递。

C++11 std::functional 是一种通用、多态的函数封装,它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,它也是对C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),换句话说,就是函数的容器。当我们有了函数的容器之后就能更加方便地将函数,函数指针作为对象进行处理。
支持4中函数的封装:

  1. 普通函数
  2. 匿名函数
  3. 类成员函数
  4. 仿函数
#include<functional> //引用functional头文件。
int test(int n)
{   
    cout<<n<<endl;
    return n++;
}
class CTest{
public:
	CTest(int a){this->a = a;};
	int test(int a)
	{
		cout<<"this is a member function a="<<a<<endl;
		return a++;
	};
	int operator()(int n)
	{
		cout<<n<<endl;
		return n;
	}
	
private:
	int a;
};
int main()
{
	//使用函数对象包装器
	//std::function<返回值类型(参数类型)>
    //普通函数
    std::function<int(int)>f = test;
    f(123);
    
    //匿名函数:
    std::function<int(int)>f2 = [](int n)->int{
	    cout<<"this is a anonymous function n="<<n<<endl;
	    return n++;
    };
    f2(123);
	
	//类成员函数:
	std::function<int(CTest*, int)> f3=&CTest::test;
	CTest t(10);
	f3(&t,10);
	
	//仿函数:
	std::function<int(CTest*, int)> f4=&CTest::operator();
	CTest t2(11);
	f3(&t2,11);
}

4.2 bind机制

bind机制可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体,这种机制在回调函数的使用过程中也颇为有用。

(1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的
(2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的
(3)bind的返回值是可调用实体,可以直接赋给std::function对象
(4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的
(5)类的this可以通过对象或者指针来绑定

int add(int a, int b, int c, int d)
{   
	int ret = a+b+c+d;
	cout<<ret<<endl;
	return ret;
}
int main()
{
	auto f1 = std::bind(add,1,2,3,4);
	f1();

	auto f2 = std::bind(add, 1, std::placeholders::_1,3,4);
	f2(20);
	
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值