C++之lambda表达式的介绍

Posted 遥远的歌s

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++之lambda表达式的介绍相关的知识,希望对你有一定的参考价值。

lambda表达式的引出

如果我们想要对一组数据进行排序,则可以使用sort函数来排序,但是对于自定义类型调用此函数的时候,我们需要来自定义比较大小的逻辑,比如下面这个例子

class A

public:
  A(int a = 0)
    :_a(a)
  
  //自己定义比较大小的逻辑函数
  bool operator>(const A& a) const
  
    return _a > a._a;
  
  bool operator<(const A& a) const
  
    return _a < a._a;
  
public: //方便下面打印这里给成公有
  int _a;
;
void test()

  A array[] = 1,3,5,7,2,4,6;//类型为A的一个对象数组
  //默认从小到大排序
  sort(array,array + sizeof(array)/sizeof(array[0]));
  for(auto &e:array)
    cout<<e._a<<" ";
  cout<<endl;
  //通过greayer<A>()来进行从大到小的排序
  sort(array,array + sizeof(array)/sizeof(array[0]),greater<A>());
  for(auto &e:array)
    cout<<e._a<<" ";
  cout<<endl;

输出的结果如下图

除了此方法之外也可以使用一个仿函数来进行大小比较的一个定义

class A

public:
  A(int a = 0)
    :_a(a)
  
public:
  int _a;
;
//构造一个仿函数
struct Less

  bool operator()(const A& a1,const A& a2)
  
    return a1._a < a2._a;
  
;
struct Greater

  bool operator()(const A& a1,const A& a2)
  
    return a1._a > a2._a;
  
;
void test()

  A array[] = 1,3,5,7,2,4,6;
  //通过仿函数进行比较排序
  sort(array,array + sizeof(array)/sizeof(array[0]),Less());
  for(auto &e:array)
    cout<<e._a<<" ";
  cout<<endl;
  //通过仿函数进行比较排序
  sort(array,array + sizeof(array)/sizeof(array[0]),Greater());
  for(auto &e:array)
    cout<<e._a<<" ";
  cout<<endl;

上述仿函数实现的结果如下图

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了 极大的不便。因此,在C++11语法中出现了Lambda表达式。

lambda表达式

将上述例子写成一个lambda表达式如下:

class A

public:
  A(int a = 0)
    :_a(a)
  
  bool operator>(const A& a) const
  
    return _a > a._a;
  
  bool operator<(const A& a) const
  
    return _a < a._a;
  
public:
  int _a;
;
void test()

  A array[] = 1,3,5,7,2,4,6;
  //lamabda表达式
  sort(array,array + sizeof(array)/sizeof(array[0]),[](const A& a1,const A& a2)->bool
  
    return a1 < a2;
  );
  cout<<"lamdba:"<<endl;
  for(auto &e:array)
    cout<<e._a<<" ";
  cout<<endl;

输出结果如下:

lambda表达式语法
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type statement

  1. [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来 的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  2. (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起 省略 mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修 饰符时,参数列表不可省略(即使参数为空)。
  3. ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分 可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
  4. statement:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

lambda表达式开头为[],当编译器看到以[]开头的时候,就会判断它是lambda表达式。并且lambda表达式没有名字,它有一对大括号,所以它存在一个函数体,因此它是一个匿名函数。
其中:

  • []捕捉列表中一般捕捉的都是父类作用域中的局部变量,它可以是空的。一些常见的捕捉方式如下:
  1. [var]:表示值传递方式捕捉变量var
  2. [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  3. [&var]:表示引用传递捕捉变量var
  4. [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
  5. [this]:表示值传递方式捕捉当前的this指针
  • ()参数列表中和平常的函数中的参数列表一样,没有任何区别
  • ->返回值同理和正常函数的返回值一样,如果需要返回某一类型,则只需要对应该类型即可
  • 函数题部分,李米娜可以用捕捉列表中捕捉道德参数也可以用参数列表中给的参数,但是如果没有mutable修饰的话,是不能在函数体中需改捕捉列表中参数的值得,因为它默认为const属性。

举例
1.[var]值传递捕捉

void test()

	int a = 5;
	int b = 10;
	auto fun1 = [a,b](int c)->intreturn a + b + c;;//lambda表达式定义
	fun1(20);//使用,结果为a+b+c

运行结果:

2.[=]值捕捉

void test()

	int a = 1;
	int b = 2;
  //以值得形式进行捕捉
  auto fun = [=](int num)mutable->int
    a = 5;
    b = 10;
    return a + b + num;
  ;
  cout<<fun(10)<<endl;
  int c;//上述lambda表达式不能捕捉到c,lambda表达式只能捕捉在定义它之前已经定义过的变量

运行结果:

3.[&]引用传递捕捉所有父类作用域中的变量
注意:如果是通过引用的形式进行捕捉,不需要mutable也可以改变捕捉到的变量的值

void test()

	int a = 1;
	int b = 2;
  cout<<a<<" "<<b<<endl;
  //以引用传递形式进行捕捉
  auto fun = [&](int num)mutable->int
    //a,b的值会改变
    a = 5;
    b = 10;
    return a + b + num;
  ;
  cout<<fun(10)<<" "<<a<<" "<<b<<endl;

运行结果:

4. [&var]引用传递捕捉变量var

  • [=,&var]形式
	int a = 1;
	int b = 2;
  cout<<a<<" "<<b<<endl;
  //以值形式捕捉所有变量,以引用传递形式捕捉变量a
  auto fun = [=,&a](int num)->int
    //a的值会改变
    a = 5;
    return a + b + num;
  ;
  cout<<fun(10)<<" "<<a<<" "<<b<<endl;

运行结果:

  • [&,var]形式
void test()

	int a = 1;
	int b = 2;
  cout<<a<<" "<<b<<endl;
  //除过a之外,以他这个i以引用的形式进行捕捉
  auto fun1 = [&,a](int num)->int
    //a的值会改变
    b = 10;
    return a + b + num;
  ;
  cout<<fun1(10)<<" "<<a<<" "<<b<<endl;

运行结果

5.[this]捕捉当前的this指针

class C

public:
  void print()
  
    int a = 1;
    int b = 2;
    //以值形式捕捉a,b,再捕捉当前的this指针
    //其实也可以不加this,因为这里的父类作用域可能是直接父类作用域,也可能是整个类的作用域
    //a,b属于print函数的局部域,_a属于类域C,但是它门都属于这个lambda表达式的父类作用域	
    auto fun1 = [=,this](int num)->void
      cout<<a<<" "<<b<<" "<<this->_a<<endl;
    ;
  
public:
  int _a = 3;
;

注意

  1. 父作用域指包含lambda函数的语句块
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
    比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量 [&,a, this]:值 传递方式捕捉变量a和this,引用方式捕捉其他变量
  3. 捕捉列表不允许变量重复传递,否则就会导致编 译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
  4. 如果不是局部域,捕捉列表中不能指定具体的变量,但是可以写成=或&。
  5. lambda表达式之间不能相互赋值,即使看起来类型相同,但是允许拷贝构造,同时可以将lambda表达式赋值给相同类型的函数指针
    比如:
void (*pfun)();
void test()

	auto fun1 = [](int a,int b)->intreturn a + b;;
	auto fun2 = [](int a,int b)->intreturn a + b;;
	//fun1 = fun2  这是不行的,因为lambda表达式之间不能相互赋值
	// 允许使用一个lambda表达式拷贝构造一个新的副本    
	auto fun3(fun2); 
	auto fun4 = fun2; 
	//可以将lambda表达式赋值给相同类型的函数指针 
	pfun = fun1;

函数对象与lambda表达式

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象。

class Rate 
 
public:    
	Rate(double rate)
		: _rate(rate)    
	
 
    double operator()(double money, int year)    
     
    	return money * _rate * year;
    
private:    
	double _rate; 
;
int main() 
    
	// 函数对象    
	double rate = 0.49;    
	Rate r1(rate);    
	r1(10000, 2);
    // lambda对象   
    auto r2 = [=](double monty, int year)->doublereturn monty*rate*year; ;    r2(10000, 2);    
    return 0; 

从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可以直接将该 变量捕获到

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一 个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

以上是关于C++之lambda表达式的介绍的主要内容,如果未能解决你的问题,请参考以下文章

c++ 11之 lambda表达式的使用

c++ 11之 lambda表达式的使用

C++学习:6补充

C++ lambda表达式(函数指针和function)

Java之线程池和Lambda表达式

C++ lambda 表达式