C++:从捕获函数参数的函数返回 lambda 表达式

C++: Return a lambda expression from a function that captures parameter of the function

本文关键字:函数 返回 lambda 表达式 参数 C++      更新时间:2023-10-16

下面的函数应该获取多项式的系数并从中创建时间函数:

std::function<double(double)> to_equation(const std::vector<double>& coefficients)
{
return [coefficients](double t)
{
auto total = 0.0;
for (int i = 0; i < coefficients.size(); i++)
{
total += coefficients[i] * pow(t,i);
return total;
}
};
}

它应该可用如下:

std::vector<double> coefficients = {1.0,2.0,3.0};
auto f = to_equation(coefficients);
auto value = f(t);

然而,代码并没有按预期工作,因为在执行f(t)时,不是使用传递给to_equation(coefficients)的系数,而是从上下文中神奇地捕获了一些完全不同的值。发生了什么,我该如何解决?

好吧,您返回的 lambda 按值捕获coefficients。如果将某个向量传递给to_equation函数,则将复制所有值,并且 lambda 将不再引用原始向量。

我建议这个解决方案:

// auto is faster than std::function
auto to_equation(const std::vector<double>& coefficients)
{
// Here, you capture by reference.
// The lambda will use the vector passed in coefficients
return [&coefficients](double t)
{
// ...
};
}

但是,有时必须处理如下代码:

std::function<double(double)> f;
{
std::vector<double> coeff{0.2, 0.4, 9.8};
f = to_equation(coeff);
}
auto result = f(3);

这很糟糕,载体coeff寿命不够长,我们在载体被破坏后再引用它。

我建议将此重载添加到您的函数中:

// when a vector is moved into coefficients, move it to the lambda
auto to_equation(std::vector<double>&& coefficients)
{
// Here, you capture by value.
// The lambda will use it's own copy.
return [coeff = std::move(coefficients)](double t)
{
// ...
};
}

然后,可以通过两种方式调用函数:

std::vector<double> coeff{0.2, 0.4, 9.8};
auto f1 = to_equation(coeff); // uses reference to coeff
auto f2 = to_equation({0.2, 0.4, 9.8}) // uses value moved into the lambda

您可以按引用而不是按值捕获。但是,当然,如果底层向量超出范围并在调用 lambda 之前被销毁,那么您手上就会一团糟。

最安全的做法是使用std::shared_ptr<std::vector<double>>而不是普通向量,并按值捕获它。然后,lambda 本质上将始终以最新的系数集为食,如果在对底层向量的所有其他引用(从计算它们的任何代码(超出范围之后调用它,则不会爆炸。

(当然,你必须记住,如果 lambda 被复制,这里会发生什么,因为原始 lambda 的所有副本都将使用相同的向量(。

有关更多信息,请打开C++书中的章节,其中解释了使用 lambda 时按值捕获和按引用捕获之间的区别。