ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++11新特新-lambda

2021-03-05 16:57:06  阅读:138  来源: 互联网

标签:11 return 函数 val 新特新 c++ int 捕捉 lambda


c++11新特性中加入了lambda表达式

在qt中使用lambda的前提是在.pro文件中加入:CONFIG+=c++11

lambda的语法定义

[捕捉列表](参数列表)mutable->return-type{函数体}
  • 捕捉列表:总是出现在lambda函数的开始,实际上,[]是lambda的引出符,编译器根据该引出符判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量以供lambda函数使用;
  • 参数列表:与普通函数的参数列表一样。如果不需要参数传递,则可以连同括号“()”一起省略;
  • mutable:mutable修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略,即使参数为空;
  • ->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;
  • 函数体:内容与普通函数一样,不过lambda函数和普通函数最大的区别说:除了可以使用参数列表的参数之外,还可以使用所有捕捉到变量

捕捉列表详解

捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(值传递或引用传递),捕捉列表由多个捕捉项组成,并以逗号分隔,具体有一下几种方式:

  1. [var] 表示值传递方式捕捉变量var
  2. [=] 表示值传递方式捕捉所有父作用域的变量(包括this)
  3. [&var] 表示引用传递捕捉变量var
  4. [&] 表示引用传递方式捕捉所有父作用域的变量(包括this)
  5. [this] 表示值传递方式捕捉当前的this指针

捕捉列表还可以进行组合,例如以下方式:

  • [=,&a,&b] 表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量
  • [&,a,this] 表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量

不过值得注意的是,捕捉列表不允许变量重复传递。下面一些例子就是典型的重复,会导致编译时期的错误。例如:

  • [=,a] 这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的
  • [&,&this] 这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复

lambda有趣的使用1

在没有lambda之前,以前的代码写法可以如下:比较复杂

#include<iostream>
using namespace std;
typedef enum
{
add = 0,
sub,
mul,
divi
}type;
class Calc
{
public:
Calc(int x, int y):m_x(x), m_y(y){}
int operator()(type i)
{
switch (i)
{
case add:
return m_x + m_y;
case sub:
return m_x - m_y;
case mul:
return m_x * m_y;
case divi:
return m_x / m_y;
}
}
private:
int m_x;
int m_y;
};
int main()
{
Calc addObj(10, 20);
cout<<addObj(add)<<endl;
return 0;
}

现在我们有了Lambda这个利器,那是不是可以重写上面的实现呢?看代码:

#include<iostream>
using namespace std;
typedef enum
{
add = 0,
sub,
mul,
divi
}type;
int main()
{
int a = 10;
int b = 20;
auto func = [=](type i)->int {
switch (i)
{
case add:
return a + b;
case sub:
return a - b;
case mul:
return a * b;
case divi:
return a / b;
}
};
cout<<func(add)<<endl;
}

可以看到,代码简洁多了

lambda有趣的使用2

先看看下面这段代码:

#include<iostream>
using namespace std;
int main()
{
int j = 10;
auto by_val_lambda = [=]{ return j + 1; };
auto by_ref_lambda = [&]{ return j + 1; };
cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //11
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //11
++j;
cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //11
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //12
return 0;
}

为什么第三个输出不是12呢?

原因:在by_val_lambda中,j被视为一个常量,一旦初始化后不会再改变(可以认为之后只是一个跟父作用域中j同名的常量),而在by_ref_lambda中,j仍然在使用父作用域中的值。所以,在使用Lambda函数的时候,如果需要捕捉的值成为Lambda函数的常量,我们通常会使用按值传递的方式捕捉;相反的,如果需要捕捉的值成成为Lambda函数运行时的变量,则应该采用按引用方式进行捕捉。

再来看看下面这段代码:

#include<iostream>
using namespace std;
int main()
{
int val = 0;
// auto const_val_lambda = [=](){ val = 3; }; wrong!!!
auto mutable_val_lambda = [=]() mutable{ val = 3; };
mutable_val_lambda();
cout<<val<<endl; // 0
auto const_ref_lambda = [&]() { val = 4; };
const_ref_lambda();
cout<<val<<endl; // 4
auto mutable_ref_lambda = [&]() mutable{ val = 5; };
mutable_ref_lambda();
cout<<val<<endl; // 5
return 0;
}

这段代码主要是用来理解Lambda表达式中的mutable关键字的。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。按照规定,一个const的成员函数是不能在函数体内修改非静态成员变量的值。例如上面的Lambda表达式可以看成以下仿函数代码:

class const_val_lambda
{
public:
const_val_lambda(int v) : val(v) {}
void operator()() const { val = 3; } // 常量成员函数
private:
int val;
};

对于const的成员函数,修改非静态的成员变量,所以就出错了。而对于引用的传递方式,并不会改变引用本身,而只会改变引用的值,因此就不会报错了。都是一些纠结的规则。慢慢理解吧。

标签:11,return,函数,val,新特新,c++,int,捕捉,lambda
来源: https://blog.csdn.net/u011720508/article/details/114402873

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有