一尘不染

什么是 C++11 中的 lambda 表达式?

c++

什么是 C++11 中的 lambda 表达式?我什么时候用一个?他们解决了哪类问题在引入之前是不可能的?

几个例子和用例会很有用。


阅读 178

收藏
2022-02-19

共1个答案

一尘不染

C++ 包含有用的泛型函数,例如std::for_eachand std::transform,非常方便。不幸的是,它们使用起来也很麻烦,特别是如果您要应用的函子对于特定函数是唯一的。

#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

如果您只f在那个特定的地方使用一次,那么编写整个课程只是为了做一些微不足道的事情似乎有点矫枉过正。

在 C++03 中,您可能很想编写类似以下的内容,以保持函子本地化:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

但是这是不允许的,f不能传递给 C++03 中的模板函数。

新的解决方案

C++11 引入了 lambda,允许你编写一个内联的匿名仿函数来替换struct f. 对于小的简单示例,这可以更清晰地阅读(它将所有内容保存在一个地方)并且可能更易于维护,例如以最简单的形式:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda 函数只是匿名函子的语法糖。

返回类型

在简单的情况下,会为您推导出 lambda 的返回类型,例如:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

但是,当您开始编写更复杂的 lambda 表达式时,您将很快遇到编译器无法推断返回类型的情况,例如:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

要解决此问题,您可以使用以下方法显式指定 lambda 函数的返回类型-> T

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

“捕获”变量

到目前为止,除了传递给其中的 lambda 之外,我们还没有使用任何其他变量,但我们也可以在 lambda 中使用其他变量。如果您想访问其他变量,您可以使用捕获子句([]表达式的),到目前为止,这些示例中尚未使用该子句,例如:

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

您可以通过引用和值来捕获,您可以分别使用&和指定=

  • [&epsilon, zeta]通过引用捕获 epsilon,按值捕获 zeta
  • [&]通过引用捕获 lambda 中使用的所有变量
  • [=]按值捕获 lambda 中使用的所有变量
  • [&, epsilon]通过引用捕获 lambda 中使用的所有变量,但按值捕获 epsilon
  • [=, &epsilon]通过值捕获 lambda 中使用的所有变量,但通过引用捕获 epsilon

operator()默认情况下生成const,这意味着默认情况下将const在您访问它们时进行捕获。这具有相同输入的每个调用都会产生相同结果的效果,但是您可以将 lambda 标记为mutable请求operator()生成的不是const

2022-02-19