【C++】C++11语法 ~ lambda 表达式

这篇具有很好参考价值的文章主要介绍了【C++】C++11语法 ~ lambda 表达式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🌈欢迎来到C++专栏~~ lambda 表达式


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

一. 概念

自 C++11 开始,C++ 有三种方式可以像函数使用的对象 / 类型:

函数指针
仿函数
Lambda 表达式

此处要注意:greater后面的括号是什么时候要加上?(容易混淆)

  • 首先我们要清楚,传的是对象还是类型:传的是对象就要加(),如果是类型就不用

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言
言归正传,lambda 表达式本质上就是一个匿名函数

这里举个例子:

struct Items
{
	string _name;  //名字
	double _price; //价格
	int _num;      //数量
};

如果要对若干对象分别按照价格和数量进行升序、降序排序

可以使用 sort 函数,但由于这里待排序的元素为自定义类,如果想按照我们的数据进行排序,只能通过仿函数来实现了,那岂不是要实现6个仿函数?
有点麻烦了

struct ComparePriceLess//价格降序
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price < g2._price;
	}
};
struct ComparePriceGreater//价格升序
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 300 }, { "香蕉", 3.3, 100 }, { "橙子", 2.2, 1000 }, { "菠萝", 1.5, 1 } };
	sort(v.begin(), v.end(), ComparePriceLess());    //价格升序
	sort(v.begin(), v.end(), ComparePriceGreater()); //价格降序
	return 0;
}

为此lambda 表达式就横空出世了

二. 语法

lambda 表达式定义:

[capture-list] (parameters) mutable -> return-type { statement }

表达式各部分说明:

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

除了捕获列表,Lambda表达式的其它地方其实和普通的函数基本一样,lambda 参数列表和返回值类型都是可有可无的,但捕捉列表和函数体是不可省略的,因此最简单的lambda函数如下:

int main()
{
	[]{}; //最简单的lambda表达式
	return 0;
}

再举个函数相加的例子:

int main()
{
	//两个函数相加
	auto add1 = [] (int a, int b)->int{return a + b; };
	cout << add1(1, 2) << endl;

	//省略返回值
	auto add2 = [](int a, int b){return a + b; };
	cout << add1(1, 2) << endl;

	return 0;
}

如果我们要不传参数,该怎么样实现呢?

三. 捕获方式

Lambda 表达式最基本的两种捕获方式是:按值捕获和按引用捕获

  • [var]:值传递捕捉变量var
  • [=]:值传递捕获所有父作用域中的变量(成员函数包括this指针)
  • [&var]:引用传递捕捉变量var
  • [&]:引用传递捕捉所有父作用域中的变量(成员函数包括this指针)

注意:

  1. 1️⃣父作用域要包含lambda函数语句(一般指的是当前所在的函数(栈帧)
  2. 2️⃣语法上捕捉可由多个捕捉项组成,并以逗号分割(就是可以混合着来)
  //混合捕捉:a是引用捕捉,其他都是传值捕捉
  auto f1 = [=, &a]() {
  	cout << a << b << c << d << e << endl;
  };

  f1();
  1. 3️⃣捕捉列表不允许变量重复传递,否则就会导致编译错误
  2. 4️⃣在块作用域以外的lambda函数捕捉列表必须为空
  3. 5️⃣在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都 会导致编译报错。

来个例题:此处的f可以++吗?

int f = 0;
int main()
{
	int a, b, c, d, e;
	a = b = c = d = e = 1;

	//混合捕捉:a是引用捕捉,其他都是传值捕捉
	auto f1 = [=, &a]() {
		f++;
		cout << a << b << c << d << e << endl;
	};

	f1();
	return 0;
}

f为什么可以呢?

  • 因为f全局变量不存在于栈帧里,存在于静态区,哪个位置都可以用它!

复习一下:
对象的作用域和存储区域要分清楚:

  • 生命周期是和存储的区域有关系
  • 作用域(编译器编译,用的地方能否找到) 局部 —> 全局

💥相互赋值

lambda表达式之间不能相互赋值,就算是两个一模一样的也不行

lambda 表达式会被处理为函数对象,该函数对象对应的类名叫做<lambda_uuid>

类名中的uuid叫做通用唯一识别码,简单来说就是通过算法生成的一串字符串,它具有随机性和不重复性,保证在当前程序中每次生成不同的 uuid,因为 lambda 表达式底层的类名包含 uuid,这就保证了每个 lambda 表达式底层类名都是唯一的!

void (*PF)();
int main()
{
	 auto f1 = []{cout << "hello world" << endl; };
	 auto f2 = []{cout << "hello world" << endl; };
	    
	 f1 = f2;   // 编译失败--->提示找不到operator=()
	            //实例化后的两个lambda类型,类型不一样
	 auto f3(f2);
	 f3();
	 // 可以将lambda表达式赋值给相同类型的函数指针
	 PF = f2;
	 PF();
	 return 0;
}

但是lambda表达式赋值给相同类型的函数指针

就是在我们看来是一样的,但是其底层大有不同!

四. 底层实现

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,就是对()进行了重载

class Add
{
public:
	Add(int base)
		:_base(base)
	{}
	int operator()(int num)
	{
		return _base + num;
	}
private:
	int _base;
};
int main()
{
	int base = 1;

	//函数对象
	Add add1(base);
	add1(1000);

	//lambda表达式
	auto add2 = [base](int num)->int
	{
		return base + num;
	};
	add2(1000);
	return 0;
}

对反汇编进行观察:

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

当创建add对象的时候是构造函数,使用add对象的时候就是会调用 Add 类的 () 运算符重载函数

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

lambda 表达式同样如此:会调用 <lambda_uuid> 类的构造函数,在使用add2对象时,会调用<lambda_uuid>类的 ()运算符重载函数

其本质就是:lambda表达式在底层被转换成了仿函数

当我们定义一个lambda表达式后,编译器会自动生成一个类,在该类中对 () 运算符进行重载,实际 lambda 函数体的实现就是这个仿函数 operator() 的实现,在调用 lambda 表达式时,参数列表和捕获列表的参数,最终都传递给了仿函数的 operator()

五. mutable(作用不大)

在实际使用中,比如实现一个交换函数,我们用 lambda 表达式实现:

int main()
{
	auto swap2 = [x, y]() 
	{
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap2();
	return 0;
}

这里我们发现是传值传参!果然编译不通过,因为传值捕获到的变量默认是不可修改的(const):

如果要取消其常量属性,就需要在 lambda 表达式中加上 mutable 像这样:

	auto swap2 = [x, y]() mutable    //改变的是形参,实参无影响,所以没用
	{
		int tmp = x;
		x = y;
		y = tmp;
	};

但是捕捉列表是传值捕捉过来的,不影响外面的实参;所以这种方法无法完成交换功能

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言

📢写在最后

【C++】C++11语法 ~ lambda 表达式,C++ ~ 登峰造极,c++,算法,开发语言文章来源地址https://www.toymoban.com/news/detail-809187.html

到了这里,关于【C++】C++11语法 ~ lambda 表达式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++进阶】C++11(下)可变参数模板&lambda表达式&包装器

    我们紧接着上一节的讲解来进行 C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧

    2024年04月11日
    浏览(89)
  • 【C++】C++11右值引用|新增默认成员函数|可变参数模版|lambda表达式

    在C++11之前,我们只有引用的概念,没有接触到所谓的左值引用或者是右值引用这种概念,从C++11开始,增加了右值引用的概念,那么现在我们将对引用进行一个概念上的区分。在此之前我们所说的引用都是左值引用,对于左值引用相关的内容,可以去看一看博主之前写的文章

    2024年02月15日
    浏览(57)
  • Lambda表达式:简介、语法和用法

    Lambda表达式是Java 8中引入的一个重要特性,它允许开发者以更加简洁的方式编写函数式代码。在本文中,我们将深入探讨Lambda表达式的概念、语法和用法,并为每个实例提供代码演示,同时对比与传统方法的区别和优势。 Lambda表达式是一种匿名函数,它主要用于表示简单的行

    2023年04月19日
    浏览(113)
  • C++11 lambda表达式

    lambda表达式是C++11或者更新版本的一个 语法糖 ,本身不是C++开发的。但是因其便利,很值得我们学习和使用。lambda有很多叫法,有lambda表达式、lambda函数、匿名函数,本文中为了方便表述统一用 lambda表达式 进行叙述。 在C++中,为了实现泛型编程,在一个类中,我们难免遇到

    2024年02月08日
    浏览(56)
  • 【 C++11 】lambda表达式

    目录 1、lambda表达式的引入 2、lambda表达式         lambda表达式的语法         lambda表达式捕捉列表说明         使用lambda表达式排序自定义类型         lambda表达式的底层原理 1、lambda表达式的引入 在C++98中,如果想要对一个数据集合中的元素进行排序,可以

    2024年02月08日
    浏览(45)
  • C++11:lambda表达式

    lambda表达式实际上是一个匿名类的成员函数,该类由编译器为lambda创建,该函数被隐式地定义为内联。因此,调用lambda表达式相当于直接调用它的operator()函数,这个函数可以被编译器内联优化(建议)。 例如快速排序算法,STL允许用户自定义比较方式,在C++11之前,通常使用

    2024年02月14日
    浏览(29)
  • C++11的lambda表达式

    Lambda表达式是一种匿名函数,允许我们在不声明方法的情况下,直接定义函数。它是函数式编程的一种重要特性,常用于简化代码、优化程序结构和增强代码可读性。 lambda表达式的语法非常简单,具体定义如下: 举例: [ captures ] —— 捕获列表,它可以捕获当前函数作用域

    2024年02月03日
    浏览(42)
  • C++11_lambda表达式

    lambda表达式是C++11新引入的功能,它的用法与我们之前学过的C++语法有些不同。 [capture-list] (parameters) mutable - return-type { statement } [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来 判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的

    2024年02月02日
    浏览(49)
  • C++11新特性lambda 表达式

    Lambda 表达式的基本语法是:[] (参数列表) - 返回值类型 {函数体}。 方括号([])表示捕获列表,用来指定在 lambda 表达式中可以访问的外部变量。 参数列表和返回值类型与普通函数的参数列表和返回值类型相同。 函数体则是实际的代码逻辑。 不接受任何参数:[] { 函数体 } 接受

    2024年02月14日
    浏览(39)
  • 【C++11】lambda表达式 包装器

    在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法: 如果待排序元素为自定义类型,需要用户定义排序时的比较规则: 如果仿函数命名比较规范的话,像上面的命名方式的话那还好,如果遇到了像cmp1 cmp2 cmp3…这种命名方式而且还没有注释的话可以

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包