掌握现代 C++:Lambda 在 C++14、C++17 和 C++20 中的演变

深入研究Lambda 在 C++14、C++17 和 C++20 中的演变

  • 一、背景
  • 二、C++14 中的 Lambda
    • 2.1、默认参数
    • 2.2、模板参数
    • 2.3、广义捕获
    • 2.4、从函数返回 lambda
  • 三、C++17 中的 Lambda
  • 四、C++20 中的 Lambda
  • 总结

一、背景

Lambda 是现代 C++ 最受欢迎的功能之一。自从在 C++ 11 中引入以来,它们在 C++ 代码中无处不在。而且,自从它们在 C++11 中出现以来,它们已经发展并获得了重要的功能。其中一些功能有助于编写更具表现力的代码,并且由于现在使用 lambda 非常普遍,因此花时间学习可以用它们做什么是非常值得的。

本文目标是剖析 lambda 的主要演变过程,但不是所有的小细节。对 lambda 的基础知识不了解可以阅读博主的另一篇文章,有详细介绍。

lambda 的一般演变是赋予它们手动定义的函数对象的功能。
在这里插入图片描述

二、C++14 中的 Lambda

在 C++14 中,lambda 获得了 4 项主要增强功能:

  • 默认参数
  • 模板参数
  • 广义捕获
  • 从函数返回 lambda

2.1、默认参数

在 C++14 中,lambda 可以采用默认参数,就像任何函数一样:

auto myLambda = [](int x, int y = 0) { std::cout << x << '-' << y << '\n'; 
};std::cout << myLambda(1, 2) << '\n';
std::cout << myLambda(1) << '\n';

输出:

1-2
1-0

2.2、模板参数

在 C++11 中必须定义 lambda 参数的类型:

auto myLambda = [](int x){ std::cout << x << '\n'; 
};

在 C++14 中,可以让它们接受任何类型:

auto myLambda = [](auto&& x){ std::cout << x << '\n'; 
};

即使不需要处理多种类型,这对于避免重复并使代码更紧凑和可读也很有用。例如,这种 lambda:

auto myLambda = [](namespace1::namespace2::namespace3::ACertainTypeOfWidget const& widget) { std::cout << widget.value() << '\n'; 
};

变成:

auto myLambda = [](auto&& widget) { std::cout << widget.value() << '\n'; 
};

2.3、广义捕获

在 C++11 中,lambda 只能捕获其作用域中的现有对象:

int z = 42;
auto myLambda = [z](int x){ std::cout << x << '-' << z + 2 << '\n'; 
};

C++14 借助强大的lambda广义捕获,可以用几乎任何东西初始化捕获的值。示例:

int z = 42;
auto myLambda = [y = z + 2](int x) { std::cout << x << '-' << y << '\n'; 
};myLambda(1);

此代码输出以下内容:

1-44

2.4、从函数返回 lambda

Lambda 受益于 C++14 的语言功能:从函数返回,而无需指定返回类型。由于 lambda 的类型是由编译器生成的,因此在 C++11 中无法从函数返回 lambda。

/* what type should we write here ?? */ 
f()
{return [](int x){ return x * 2; };
}

在 C++14 中可以通过用作返回类型来返回 lambda。这在一段代码中间有一个大的 lambda 的情况下很有用。

void f()
{// ...int z = 42;auto myLambda = [z](int x) {// ...// ...// ...};// ...
}

可以将 lambda 打包到另一个函数中,从而引入另一个抽象级别:

auto getMyLambda(int z)
{return [z](int x) {// ...// ...// ...};
}void f()
{// ...int z = 42;auto myLambda = getMyLambda(z);// ...
}

三、C++17 中的 Lambda

C++17 为 lambda 带来了一个重大增强:它们可以声明constexpr

constexpr auto times2 = [] (int n) { return n * 2; 
};

然后,可以在编译时评估的上下文中使用此类 lambda:

static_assert(times2(3) == 6);

这在模板编程中特别有用。

注意:lambda 在 C++20 中变得更加有用。事实上,只有在 C++20 中,大多数 STL 算法才变得如此,并且它们可以与 lambda 一起使用,以创建在编译时评估的集合的复杂操作。
不过,有一个例外:std::array非变异访问操作在 C++ 14 中立即变为std::array constexpr,而在 C++17 中变为非变异访问操作constexpr

捕获 *this 的副本:lambda 在 C++17 中获得的另一个特性是捕获*this的副本的简单语法。
示例:

struct MyType{int m_value;auto getLambda()  {return [this](){ return m_value; };}
};

此 lambda 捕获this指针的副本。如果 lambda 的生存期超过对象的生存期,则可能会导致内存错误,例如:

auto lambda = MyType{42}.getLambda();
lambda();

由于MyType在第一个语句的末尾被销毁,因此第二个语句调用的lambda取消了this引用访问其m_value ,但this指向一个被销毁的对象。这会导致未定义的行为,通常是应用程序崩溃。

解决此问题是在 lambda 中捕获整个*this对象的副本。C++17 提供了语法来实现这一点。

struct MyType
{int m_value;auto getLambda() {return [*this](){ return m_value; };}
};

当然,在 C++ 14 中使用广义捕获已经可以实现相同的结果:

struct MyType
{int m_value;auto getLambda() {return [self = *this](){ return self.m_value; };}
};

只是C++17 使语法更好。

四、C++20 中的 Lambda

Lambda 在 C++ 20 中得到进一步发展,但其功能不如 C++ 或 C++ 17 那么基本。C++ 20 中 lambda 的一个增强功能是定义模板的经典语法,使它们更接近手动定义的函数对象:

auto myLambda = []<typename T>(T&& value){ std::cout << value << '\n'; };

这使得访问模板参数类型比使用 表达式(如auto&&)的 C++ lambda 模板更容易。

另一个改进是能够捕获可变参数包:

template<typename... Ts>
void f(Ts&&... args)
{auto myLambda = [...args = std::forward<Ts>(args)](){};
}

总结

本文讨论了lambda从C++14到C++20的主要改进。但也还有更多没有总结进来。这些主要功能伴随着许多小特性使 lambda 代码更易于编写。

深入研究 lambda 是更好地了解 C++ 语言的绝佳机会,这是一个值得投入的时间。

C++14
C++17
C++20
Auto return type deduction
Initializing captured variables
Generic Lambdas
Init-capture
Fold expressions
if constexpr in Lambdas
Capture initialization expression
= for capturing all variables
Consteval Lambdas

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/615553.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 嵌套查询

嵌套查询 是指在一个完整的查询语句之中&#xff0c;包含若干个不同功能的小查询&#xff1b;从而一起完成复杂查询的一种编写形式。包含的查询放在&#xff08;&#xff09;里 &#xff0c; 包含的查询出现的位置&#xff1a; 位置含义SELECT之后把查询结果作为表头使用FROM…

聊一下HashMap的底层原理

HashMap作为我们熟悉的一种集合&#xff0c;今天就来聊一下它的原理、数据结构。 1.HashMap的数据结构 JDK1.7的数据结构是数组链表&#xff0c;JDK1.7还有人在用&#xff1f;不会吧…… 说一下JDK1.8的数据结构吧&#xff1a; JDK1.8的数据结构是数组链表红黑树。 数据结…

Android 14.0 SystemUI修改状态栏电池图标样式为横屏显示

1.概述 在14.0的系统rom产品定制化开发中,对于原生系统中SystemUId 状态栏的电池图标是竖着显示的,一般手机的电池图标都是横屏显示的 可以觉得样式挺不错的,所以由于产品开发要求电池图标横着显示和手机的样式一样,所以就得重新更换SystemUI状态栏的电池样式了 如图: 2.S…

Boost之date_time

Boost.Date_Time库是一个功能丰富的时间日期处理库&#xff0c;它提供了多种用于日期和时间计算、格式化、转换和输入输出的功能。以下是该库的一些详细介绍&#xff1a; 1、日期处理&#xff1a; Boost.Date_Time库中的date类基于格里高利历&#xff08;Gregorian calendar&…

MySQL基础知识——MySQL日志

一条查询语句的执行过程一般是经过连接器、 分析器、 优化器、 执行器等功能模块&#xff0c; 最后到达存储引擎。 那么&#xff0c; 一条更新语句的执行流程又是怎样的呢&#xff1f; 下面我们从一个表的一条更新语句进行具体介绍&#xff1a; 假设这个表有一个主键ID和一个…

在视频号变现,你还停留在只能自己直播的时代吗?现在新玩法来了

大家好&#xff0c;我是电商笨笨熊 在很多人的固有印象里&#xff0c;视频号当下变现的方式还只有自己开直播带货或者是短视频带货&#xff1b;这种方式虽然变现快&#xff0c;但是对于更多的普通玩家来说相对困难&#xff0c;需要直播经验&#xff0c;自身还要具备足够的粉丝…

ML在骨科手术术前、书中、术后方法应用综述【含数据集】

达芬奇V手术机器人 近年来,人工智能(AI)彻底改变了人们的生活。人工智能早就在外科领域取得了突破性进展。然而,人工智能在骨科中的应用研究尚处于探索阶段。 本文综述了近年来深度学习和机器学习应用于骨科图像检测的最新成果,描述了其贡献、优势和不足。以及未来每项研究…

CTFshow电子取证——内存取证2

接上回 JiaJia-CP-2 2.佳佳在网页上登录了自己的邮箱&#xff0c;请问佳佳的邮箱是&#xff1f; 因为是在网页上登陆的邮箱 用iehistory插件 查看一下网页历史记录 为了方便分析&#xff0c;使用grep命令正则匹配一下 **com 的记录 vol.py -f JiaJia_Co.raw --profileWin…

Git以及Gitlab的快速使用文档

优质博文&#xff1a;IT-BLOG-CN 安装git 【1】Windows为例&#xff0c;去百度下载安装包。或者去官网下载。安装过秳返里略过&#xff0c;一直下一步即可。丌要忉记设置环境发量。 【2】打开cmd&#xff0c;输入git –version正确输出版本后则git安装成功。 配置ssh Git和s…

Matlab求矩阵的逆,3种常用方法总结

几种求逆矩阵的方法总结&#xff0c;以Matlab语言为例 *0* 引言*1* 简单描述函数实现*2* 方法调用计算对比 0 引言 最近在使用函数库求解逆矩阵的时候发现同一个矩阵使用不同的语言、不同的求解方法会产生不同精度的结果&#xff0c;特别是阶数很高的方阵&#xff0c;一些库中的…

Java 中文官方教程 2022 版(四十九)

原文&#xff1a;docs.oracle.com/javase/tutorial/reallybigindex.html JAXB 示例 原文&#xff1a;docs.oracle.com/javase/tutorial/jaxb/intro/examples.html 以下部分描述如何使用包含在 JAXB RI 捆绑包中的示例应用程序。JAXB RI 捆绑包可从jaxb.java.net获取。下载并安装…

力扣HOT100 - 56. 合并区间

解题思路&#xff1a; class Solution {public int[][] merge(int[][] intervals) {// 先按照区间起始位置排序Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);int[][] res new int[intervals.length][2];int idx -1;for (int[] interval : intervals) {//直接加入的…