购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.3 使用泛型lambda和模板lambda

在前面的章节中,我们了解了如何编写lambda表达式并将其与标准算法结合使用。在C++中,lambda基本上是未命名函数对象的语法糖,这些未命名函数对象是实现调用操作符的类,因此与所有其他函数一样,这可以通过模板实现。C++14刚好就利用了这一点,引入了泛型lambda,它不需要为参数指定实际类型,而是使用auto标识符。虽然没有使用此名称引用,但泛型lambda基本上是lambda模板,当想要使用相同的lambda但使用不同类型的参数时,它们非常有用。此外,C++20标准在这方面更进一步,支持显式定义模板lambda,这有助于解决一些使用泛型lambda仍很棘手的问题。

3.3.1 准备工作

建议在学习本节内容之前先阅读3.2节的内容,以便熟悉C++中lambda的基础知识。

3.3.2 使用方式

在C++14中,我们可以编写泛型lambda:

❍ 使用auto标识符代替lambda表达式参数的实际类型。

❍ 当我们需要使用多个仅在参数类型方面不同的lambda时。

以下示例展示了一个与std::accumulate()算法一起使用的泛型lambda,首先用于一个整数vector,然后用于字符串vector:

在C++20中,我们可以编写lambda模板:

❍ 通过在捕获子句之后的尖括号中使用模板参数列表(例如<template T>)。

❍ 如果想要达到如下目的:

❍ 限制泛型lambda只使用某些类型,例如容器或满足某个概念的类型。

❍ 确保泛型lambda的两个或多个参数确实具有相同的类型。

❍ 检索泛型参数的类型,以便我们可以创建它的实例,调用静态方法或使用它的迭代器类型。

❍ 在泛型lambda中执行完美转发。

以下示例展示了一个只能使用std::vector来调用的模板lambda:

3.3.3 工作原理

在上一小节的第一个示例中,我们定义了一个命名lambda表达式。也就是说,lambda表达式将其闭包分配给变量,然后将此变量作为参数传递给std::accumulate()函数。

这个通用算法接受begin和end迭代器,它们定义了一个range、一个要累积的初始值以及一个将range中的每个值进行累积的函数。该函数的第一个参数表示当前累积值,第二个参数表示要累积的当前值,它返回新的累积值。注意,我没有使用add这个术语,因为它可以用于除加法之外的其他事情,它还可用于计算乘积、连接或其他将值聚合在一起的操作。

本例中对std::accumulate()的两次调用几乎相同,只有参数的类型不同:

❍ 在第一个调用中,我们传递指向整数range(来自vector<int>)的迭代器、0(表示初始和),以及一个lambda(用于将两个整数相加并返回其和)。这将计算range内所有整数的和,在本例中,它是22。

❍ 在第二个调用中,我们传递指向字符串range(来自vector<string>)的迭代器、作为初始值的空字符串和一个lambda(该lambda通过将两个字符串相加并返回结果)这将生成一个字符串,该字符串包含range内的所有字符串,这些字符串依次连接在一起,对于本例,结果就是hello world!。

虽然泛型lambda在调用它们的地方可以匿名定义,但这并没有实际的意义,因为泛型lambda(正如我们前面提到的,泛型lambda是lambda表达式模板)的目的是能够被重用,如3.3.2节的示例所示。

当定义此lambda表达式时,如果要多次调用std::accumulate()而不是为lambda参数指定具体的类型(例如int或std::string),则可以使用auto标识符让编译器推断类型。当遇到参数类型具有auto标识符的lambda表达式时,编译器会生成一个具有调用操作符模板的未命名函数对象。对于本例中的泛型lambda表达式,函数对象看起来像这样:

调用操作符是一个模板,lambda表达式每个参数都用auto进行标识。调用操作符的返回类型也用auto标识,这意味着编译器将从返回值的类型推导它,编译器会通过泛型lambda的上下文识别的实际类型将操作符模板进行实例化。

C++20的模板lambda是对C++14泛型lambda的改进,在某些场景使用更方便。上一小节的第二个示例展示了一个典型的例子,其中lambda仅受接受std::vector类型参数的限制。另一个例子是你希望确保lambda的两个参数具有相同的类型。在C++20之前,很难做到这一点,但是有了模板lambda,这就非常容易了,如下所示:

使用模板lambda的另一个场景是,你需要知道参数的类型,以便可以创建该类型的实例或调用它的静态成员。使用泛型lambda,解决方案如下:

此解决方案需要使用std::decay_t和decltype。然而在C++20中,同样的lambda可以写成如下形式:

当需要在泛型lambda中进行模板完美转发时(这需要使用decltype来确定参数的类型),也会出现类似的情况:

使用模板lambda,我们可以用以下更简单的方式重写它:

从这些示例中可以看出,模板lambda是泛型lambda的改进,更容易处理本节中提到的问题。

3.3.4 延伸阅读

❍ 阅读3.2节,以了解lambda表达式的基础知识,以及如何将其与标准算法结合使用。

❍ 阅读1.1节,以了解C++中自动类型推导的工作原理。 LIgPU4MuJgJb+IEZTnCmCOxqupggNieaD5t8cuEUwMao4iDzJySpCAwusKaEYYZ+

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开