在lambda函数语法中,"捕获列表"的用途是什么?

Laz*_*zer 21 c++ lambda c++11

这个问题的答案中,作为一个例子,这是一个计算元素总和的代码std::vector:

std::for_each(
    vector.begin(),
    vector.end(),
    [&](int n) {
        sum_of_elems += n;
    }
);
Run Code Online (Sandbox Code Playgroud)

我知道 lambda函数只是无名函数.

我理解 lambda函数语法,如下所述.

我不明白为什么lambda函数需要捕获列表,而普通函数则不需要.

  1. 捕获列表提供了哪些额外信息?
  2. 为什么普通功能不需要这些信息?
  3. lambda的功能不仅仅是无名功能吗?

Mat*_*hen 26

从您提供的语法链接中,捕获列表"定义了lambda外部应该在函数体内可用的内容以及如何"

普通函数可以通过以下几种方式使用外部数据:

  1. 静态字段
  2. 实例字段
  3. 参数(包括参考参数)
  4. 全局

Lambda添加了在另一个中具有一个未命名功能的能力.然后lambda可以使用您指定的值.与普通函数不同,这可以包括来自外部函数的局部变量.

正如答案所说,您还可以指定要捕获的方式.awoodland在另一个答案中给出了一些例子.例如,您可以通过引用(如引用参数)捕获一个外部变量,并通过值捕获所有其他外部变量:

[=, &epsilon]
Run Code Online (Sandbox Code Playgroud)

编辑:

区分签名和lambda内部使用的内容非常重要.lambda的签名是参数类型的有序列表,加上返回值的类型.

例如,一元函数接受特定类型的单个值,并返回另一种类型的值.

但是,在内部它可以使用其他值.作为一个简单的例子:

[x, y](int z) -> int 
{
   return x + y - z;
}
Run Code Online (Sandbox Code Playgroud)

lambda的调用者只知道它需要一个int并返回一个int.但是,在内部它恰好按值使用另外两个变量.


Ker*_* SB 19

我们试图解决的基本问题是某些算法需要一个只接受一组特定参数的函数(int在您的示例中为一个).但是,我们希望函数能够操作或检查其他一些对象,可能是这样的:

void what_we_want(int n, std::set<int> const & conditions, int & total)
{
    if (conditions.find(n) != conditions.end()) { total += n; }
}
Run Code Online (Sandbox Code Playgroud)

但是,我们所有允许给出的算法都是一个函数void f(int).那么我们在哪里放置其他数据呢?

您可以将其他数据保存在全局变量中,也可以按照传统的C++方法编写仿函数:

struct what_we_must_write
{
    what_we_must_write(std::set<int> const & s, int & t)
    : conditions(s), total(t)
    {   }

    void operator()(int n)
    {
        if (conditions.find(n) != conditions.end()) { total += n; }
    }
private:
   std::set<int> const & conditions;
   int & total;
};
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用适当初始化的仿函数调用算法:

std::set<int> conditions;
int total;

for_each(v.begin(), v.end(), what_we_must_write(conditions, total));
Run Code Online (Sandbox Code Playgroud)

最后,闭包对象(由lambda表达式描述)就是:编写仿函数的简便方法.上述仿函数的等价物是lambda

auto what_we_get = [&conditions, &total](int n) -> void {
    if (condiditons.find(n) != conditions.end()) { total += n; } };
Run Code Online (Sandbox Code Playgroud)

短手捕获列表[=]并且[&]只捕获"所有"(分别通过值或引用),这意味着编译器为您计算出具体的捕获列表(它实际上并没有将所有内容都放入闭包对象中,而只是你需要的东西).

因此,简而言之:没有捕获的闭包对象就像一个自由函数,带有捕获的闭包就像一个带有适当定义和初始化的私有成员对象的仿函数对象.


Bra*_*vic 9

考虑将lambda表达式作为碰巧具有运算符的对象()而不仅仅是函数,这可能更好.lambda"对象"可以具有在lambda构造时记住(或"捕获")lambda外变量的字段,稍后在lambda执行时使用.

捕获列表只是这些字段的声明.

(您甚至不需要自己指定捕获列表 - [&]或者[=]语法指示编译器根据外部作用域中的哪些变量在lambda体中使用来自动确定捕获列表.)

普通函数不能包含状态 - 它不能一次"记住"参数而在另一个函数中使用.一个lambda可以.使用用户实现的()操作符(也称为"functor")的手工制作的类也可以,但在语法上却不太方便.