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

What are the differences between these 4 lambda expressions?

本文关键字:什么 区别 之间 表达式 lambda      更新时间:2023-10-16

我知道这对于非菜鸟C++开发人员来说似乎非常愚蠢,但是这 4 个 lambda 表达式之间有什么区别? 法典:

#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);
}

似乎也是这样吗?要么使用两种不同的"符号"并使用值作为闭包?

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

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

    这是一个 lambda,它接受类型value的单个参数并将其传递给MyFunction。它的返回类型从return语句推导出为MyFunction的返回类型,这是double.

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

    这与以前相同,但这次它的返回类型被明确指定为double。在这种情况下是相同的,但如果返回的MyFunction类型是其他类型,它将与第一个不同。在这种情况下,第一个将返回MyFunction返回的内容,而这个仍将返回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 相同,具有显式返回类型规范(3 和 4 之间的差异与 1 和 2 之间的差异完全相同(。

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

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

[expr.prim.lambda.capture]

如果简单捕获中的标识符显示为 lambda 声明符的参数的参数的声明符 id,则程序格式不正确。 [ 示例:

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

— 结束示例  ]

这一措辞于C++17年通过。

第一个 lambda 是通常的。

第二个 lambda 表示其返回类型为double

最后两个 lambda 不合理。value都是由值及其参数捕获的,不应尝试(有一条警告指示捕获不起作用(。它要么是参数,要么是捕获的变量(按值或引用(。

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

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

第一个和第二个 lambda 之间的区别以及第三个和第四个 lambda 之间的区别在于,您要么显式指定返回类型,要么不显式指定返回类型。在这里,它会产生相同的功能。

您的前两个 lambda 与 lambda 3 和 4 之间的区别在于捕获。但是您的示例不适合说明捕获的效果。查看以下代码(现场演示(。

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行中使用。也就是说,按值捕获意味着在行中初始化lam2v持有的值//B存储在lam2中。另一方面,在行//3中使用v的当前值。这是因为lam3持有对v的引用。