这 4 个 lambda 表达式有什么区别?

What are the differences between these 4 lambda expressions?

我知道这对于 非菜鸟 C++ 开发人员来说可能看起来很愚蠢,但这 4 个 lambda 表达式之间有什么区别? Code:

#include <iostream>
#include <math.h>
#include <functional>

inline double MyFunction(double a, double b, double c) {
    return (a + b + c);
}

inline void FunctionWrapper(std::function<double(double)> tempFunct, double value) {
    std::function<double(double)> funct;

    funct = tempFunct;

    std::cout << "result: " << funct(value) << std::endl;
}

int main()
{    
    double value = 100.0;

    FunctionWrapper([](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);

    FunctionWrapper([value](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([value](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);
}

好像也一样?使用两个不同的 "notations" 并使用值作为闭包?

第一个 lambda 是通常的。

第二个lambda表示它的return类型是double.

最后两个 lambda 表达式不正确。 value 被值及其参数捕获,不应该尝试(有一个警告表明捕获不起作用)。它要么是参数,要么是捕获的变量(按值或引用)。

我想,它们应该是这样的:

[foo](double value) { return MyFunction(value, 1.0, foo); }, value);

在这种情况下,它们都产生相同的结果。但是,它们之间存在逻辑差异。

  • [](double value) { return MyFunction(value, 1.0, 2.0); }

    这是一个 lambda,它采用 value 类型的单个参数并将其传递给 MyFunction。它的 return 类型从 return 语句推导为 MyFunction 的类型,即 double.

  • [](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    这与之前的 lambda 相同,但这次它的 return 类型明确指定为 double。在这种情况下是一样的,但如果 MyFunction 的 return 类型是其他东西,它将与第一个不同。在那种情况下,第一个会 return 什么 MyFunction returns,而这个仍然 return double.

  • [value](double value) { return MyFunction(value, 1.0, 2.0); }

    这个取决于使用的标准版本。在 C++11 和 14 中,这个捕获 main 的局部变量 value。但是,该捕获被 lambda 的参数 value 隐藏,因此它实际上是无用的。如果 lambda 被声明为例如,情况会有所不同。 [value](double v) { return MyFunction(value, 1.0, 2.0); }。这会传递捕获的 value,而不是它的参数。

    在 C++17 及更高版本中,这已更改并且实际上格式错误(编译错误)。不再允许将 lambda 参数命名为与您捕获的参数相同的名称。

    由于更改是缺陷报告 (CWG 2211),它具有追溯性,因此编译器拒绝此类代码是合法的,即使在早期的 C++ 版本中也是如此。

  • [value](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    与 lambda 数 3 相同,具有明确的 return 类型规范(3 和 4 之间的区别与 1 和 2 之间的区别完全相同)。

第二个 lambda 与第一个的不同之处在于您明确指定了 return 类型。由于第一个 lambda 的推导 return 类型相同,因此没有区别。

第三个和第四个 lambda 的格式不正确,因为它们声明了一个与捕获同名的参数。见标准规则:

[expr.prim.lambda.capture]

If an identifier in a simple-capture appears as the declarator-id of a parameter of the lambda-declarator's parameter-declaration-clause, the program is ill-formed. [ Example:

void f() {
  int x = 0;
  auto g = [x](int x) { return 0; }    // error: parameter and simple-capture have the same name
}

— end example  ]

C++17采用了这种写法。

第一个和第二个 lambda 之间以及第三个和第四个之间的区别在于您是否明确指定 return 类型。在这里它产生相同的功能。

前两个 lambda 与第三个和第四个 lambda 之间的区别在于捕获。但是您的示例不适合说明捕获的效果。看下面的代码(live demo).

int main()
{    
    double v = 100.0;

    auto lam1= [](double val) { return MyFunction(val, 1.0, 2.0); }; //A
    auto lam2= [v](double val) { return MyFunction(v, 1.0, 2.0); }; //B
    auto lam3= [&v](double val) { return MyFunction(v, 1.0, 2.0); }; //C

    FunctionWrapper( lam1, v++ ); //1
    FunctionWrapper( lam2, v++ ); //2
    FunctionWrapper( lam3, v++ ); //3
    std::cout << "v: " << v << '\n';
}

虽然 lam1 不捕获任何内容,但 lam2 按值捕获 vlam3 按引用捕获 v。三个 FunctionWrapper 调用的输出分别为 103、103、106。这是因为尽管 v 在行 //1 中发生了变化,但在行 //2 中使用的是旧值。也就是说,按值捕获意味着值 vlam2 行初始化时保持 //B 存储在 lam2 中。另一方面,在 //3 行中,使用了 v 的当前值。这是因为 lam3 持有对 v.

的引用