欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-10 19:34:12
目录用法介绍捕获列表 []lambda表达式延迟调用易出错细节分析lambda表达式中的mutable (易变的)lambda表达式的类型及存储lambda表达式再演示和优点总结用法介绍lambda表达式是一种可调用对象。它定义了一个函数,并且可以捕获一定范围内的变量。auto f = [](int a)->int { return a+1};cout << f(1) << endl; //输出 2特点:是....

目录

用法介绍

捕获列表 []

 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; 
})

 

本文地址:https://blog.csdn.net/baidu_41388533/article/details/110844621

相关标签: C++11/14/17