(C++11/14/17学习笔记):lambda表达式的使用

目录

用法介绍

捕获列表 []

 lambda表达式延迟调用易出错细节分析

lambda表达式中的mutable (易变的)

lambda表达式的类型及存储

lambda表达式再演示和优点总结

用法介绍

  • lambda表达式是一种可调用对象
  • 它定义了一个函数,并且可以捕获一定范围内的变量
auto f = [](int a)->int {
    return a+1
};

cout << f(1) << endl; //输出 2
  • 特点:
    • 是个匿名函数,也可以理解为“可调用的代码单元” ,或者理解成未命名的内联函数
    • 它有一个返回类型一个参数列表一个函数体
    • 与函数不同的是,lambda表达式可以在函数的内部定义,这是常规函数做不到的。
  • 格式:
[捕获列表](参数列表)->返回类型(函数体);
  • 注意结尾有分号
  • 因为很多时候lambda表达式返回值类型特别明显,所以允许lambda表达式返回值类型省略,编译器可以自动推导。
  • 编译器不是总能推断出返回值类型,如果编译器不能推断出类型就会报错,这个时候就需要你显示的给出返回值类型。
auto f = [](int a = 10) {
    return a+1
};

cout << f(1) << endl; //输出 11
  • 没有参数的时候,参数列表可以省略,甚至()也能省略,所以如下写法也是合法的:
auto f1 = [](){ return 1; };

auto f2= [] { return 2; };

cout << f1() << endl;  //输出1

cout << f2() << endl;  //输出2
  • 捕获列表和函数体不能省略,必须时刻包含。
  • lambda调用方法和普通函数相同,都是使用()这种函数调用运算符。
  • lambda表达式可以不返回任何类型,不返回的类型就是 void
auto f = [] {};
  • lambda表达式函数体末尾分号不能省略

捕获列表 []

[捕获列表]:通过捕获列表来捕获一定范围内的变量,范围是什么意思?

a[]  不捕获任何变量

int i = 9;

auto f = [] {
    return i;
};
  • 编译报错(无法捕获外部变量),不认识i 在哪里定义。
  • 注意:a[]  不捕获任何变量,但不包括静态局部变量。lambda表达式可以直接使用局部静态变量(局部静态变量不需要捕获的)
static int i = 9;

auto f = [] {
    return i;
};

[&] 捕获外部作用域中的所有变量,并作为引用在函数体内使用

int i = 9 ;

auto f = [&] {
    i = 5;
    return i;
};

cout << f() << endl;  //输出 5
cout << i << endl;  //输出 5
  • 因为 & 的存在,那么就允许给 i 赋值,从而就改变了 i 的值。
  • 注意 i 的范围,其他地方调用f(),保证其内部的变量有效。

[=]:捕获外部作用域中所有变量,并作为副本(按值)在函数中使用,也就是可以用它的值,但不允许给它赋值

int  i = 9;

auto f = [=] {
     // i = 5; 非法,不可以给它赋值,因为是以值的方式捕获。
    return i;
};

cout << f() << endl; 输出9

[this] :一般用于类中,捕获当前类中的this指针,让lambda表达式有和当前类成员函数同样的访问权限。

  • 如果 [] 中已经使用了 & 或者 = ,那么默认就已经使用了 this ,捕获this 的目的是为了在lambda表达式中使用当前类的成员函数和成员变量。
class Te {
public:
	int m__ = 5;
	void func(int x, int y){
	auto my_lambda = [this]{   //无论是this,还是 & 或者 = 都可以达到访问类成员的目的
		
		return m__;  //因为有了this,这个访问是合法的
	  };	
	}
};

int main(){
    Te t;
    t.func(3, 4);   //输出 5
    return 0;
}

[变量名] :如果是多个变量名。彼此之间用 逗号分隔。[变量名] 表示按值捕获变量名代表的变量,同时不捕获其他变量。

class Te {
public:
	int m__ = 5;
	void func(int x, int y){
	auto my_lambda = [this,x ,y ]{  
		m__ = x + y;  //捕获x, y,可以使用,不能修改x,y的值

		return m__;  
	  };	
	}
};

[&变量名] :如果是多个变量名。彼此之间用 逗号分隔。[变量名] 表示按引用捕获变量名代表的变量,同时不捕获其他变量。

class Te {
public:
	int m__ = 5;
	void func(int x, int y){
	auto my_lambda = [this,x ,y ]{  
		x = 5;
        y = 5;  
		return m__;  
	  };	
	}
};

[= , &变量名]  :按值捕获所有外部变量,但按引用捕获&中所指的变量,这里这个=必须写在开头位置,开头这个位置表示默认捕获方式

  • 也就是说,这个捕获列表,第一个位置表示的是默认捕获方式(隐式捕获方式),后续其他的都是显式捕获方式
class Te {
public:
	int m__ = 5;
	void func(int x, int y){
	auto my_lambda = [this, &x ]{  
		x = 5;  //显示指定引用方式捕获,可以修改
        y = 5;  //默认按值捕获,不能修改  
		return m__;  
	  };	
	}
};

[&, 变量名]:按引用来捕获所有外部变量,但按值来捕获变量名所代表的变量,这里这个&必须写在开头位置,开头表示的是默认捕获方式。

class Te {
public:
	int m__ = 5;
	void func(int x, int y){
	auto my_lambda = [&, x ]{  
		x = 5;  //显示指定按值捕获,不能修改 
        y = 5;  //默认指定引用方式捕获,可以修改
		return m__;  
	  };	
	}
};

 lambda表达式延迟调用易出错细节分析

intx=5;
auto f = [=]
{
	return x;
};

x=10;
cout << f() << endl;
  • 上面输出为 5 。
  • 当遇到auto这一 行,也就是在捕获的这个时刻,x的值就已经被复制到了这个f 中了。
  • 凡是按值捕获的外部变量,在lambda表达式定义的这个时刻,所有这些外部变量值就被复制了一份存储在lambda表达式变量中。
  • 如果想要使用最新的值,则使用 & 进行捕获。

lambda表达式中的mutable (易变的)

intx=5;

auto f = [=]()mutable{
	x=6;  //可以修改
	return x;
}
  • 注意mutable前面的圆括号不能省略。

lambda表达式的类型及存储

  • c++11中lambda表达式的类型被称呼为“闭包类型(Closure Type)”。
  • 闭包:函数内的函数(可调用对象)。本质上就是lambda表达式创建的运行时期的对象。
  • lambda表达式是一种比较特殊的,匿名的,类类型【闭包类】的对象(也就是定义了一个类类型,又生成一个匿名的类类型的对象(闭包)。
    • 我们可以认为它是一个带有operator()的类类型对象,也就是仿函数(函数对象)。
  • 所以,我们也可以用 std::function 和 std::bind 来保存和调用lambda表达式。每个lambda都会触发编译器给咱们生成独一无二的类类型对象。
  • lambda表达式这种语法,是我们可以就地定义匿名函数(就地封装短小的功能闭包)。
auto f  = []{};
  • f 是个未命名的类类型对象
  • 捕获到的变量类似于作为这个匿名的类类型对象的成员变量
std::function<int(int)> fc1 =  [](int v){
	return v;
};
std::cout << fc1(6) <<std::endl; //输出6


std::function<int(int)> fc2 =  std::bind([](int v){ //bind第一个参数是函数指针(函数对象),第二个参数开始 就是真正的函数参数。
	return v;
	},
	16
);
std::cout << fc2(1) <<std::endl; //输出16
  • 捕获任何变量的lambda表达式,也就是捕获列表为空,可以转换成一个普通的函数指针。
using functype = int(*)int);//定义一个函数指针类型;

functype fp = [](int tv){
		return tv;
		};
cout << fp(17)<< endl;  //输出17

语法糖概念

  • 语法糖:一种便捷写法的意思。
int a[5];

a[0]=1; //便捷写法(语法糖)
*(a+1) = 3://a[1]=3
  • 语法糖这个词,让我们写的代码更简单,看起来也更容易理解,有效的减少些代码出错的机率。
  • 语法糖是指基于语言现有特性,构建出一个东西,程序员用起来会很方便。但它没有增加语言的原有功能。
  • 所以,这个lambda表达式,大家也可以看成是定义仿函数闭包(函数中的函数)。

lambda表达式再演示和优点总结

  • for_each:是个函数模板
void myfunc(int i){
	cout << i << endl;
}
...
//在main中调用
vector<int> myvector = { 10, 20, 30, 40, 50 };

for_each(myvector.begin(), myvector.end(), myfunc);
//输出 10 20 30 40 50 
  • 使用lambda表达式
vector<int>myvector={10,20,30,40,50};
int sum = 0;

for_each(
    myvector.begin(), myvector.end(), [&sum](int val){
	                                    sum += val;
	                                    cout << val << endl;
                                        }
);

cout << sum << endl;  //输出150

find_if简介:函数模板find_if

  • 用来查找一个东西,查什么东西呢?取决于它的第三个参数,它的第三个参数也是个函数对象(lambda表达式)。
vector<int> myvector={10, 20, 30, 40, 50};

auto result = find_if(myvector.begin(), myvector.end(), [](int val){
    cout << val << endl;
    return false; //只要我返回false,那么find_if就不停的遍历myvecotr。一直到返回true或者遍历完为止。
})
  • 如果find if第三个参数这个可调用对象(lambda)返回true,find_if就停止遍历。
vector<int> myvector={10, 20, 30, 40, 50};

auto result = find_if(myvector.begin(), myvector.end(), [](int val){
    cout << val << endl;
    if(val > 15)
        return true; //返回true,停止遍历
    return false; 
})

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值