如何在C++中使用单个模板参数传递两个 lambda 函数

How to pass two lambda functions using a single template parameter in C++

本文关键字:两个 函数 lambda 参数传递 C++ 单个模      更新时间:2023-10-16

我正在编写一个程序,以数字方式计算voigt分布的值。但是,当我尝试使用单个类模板参数传递高斯和洛仑兹函数时,我遇到了一个问题F,即使它们是同一类型。

当我使用两个模板参数时,比如F1F2,它就像一个魅力。但是只要只有一个错误,g++ 就会抛出一个错误。将 lambda 立即作为splot的(卷积)参数传递是没有帮助的。

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include <functional>
using namespace std;
#define MIN -10.
#define MAX 10.
#define EPS 0.01
template <typename T, class F> T trapezoid(F f, T a, T b, T eps) {
T result = T(0);
while (a <= b) {
result += f(a);
a += eps;
}
result -= (f(a) + f(b)) / T(2);
return result * eps;
}
template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {
auto s = [g, l, x, sigma, gamma](double x_prime)->double {
return g(x_prime, sigma) * l(x - x_prime, gamma);
};
return trapezoid(s, MIN, MAX, EPS);
}
int main (void) {
double x = 0., sigma = 1.5, gamma = 0.1;
auto gauss = [](double x, double sigma)->double {
return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
};
auto lorentz = [](double x, double gamma)->double {
return gamma / (M_PI*(x*x + gamma*gamma));
};
cout << "x: " << x << endl << "V(x): " <<
splot(gauss, lorentz, x, sigma, gamma) << endl;
return 0;
}

我建议您使用如下所示的std::function<>

typedef std::function<double(double,double)> func;
func gauss = [](double x, double sigma)->double {
return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
};
func lorentz = [](double x, double gamma)->double {
return gamma / (M_PI*(x*x + gamma*gamma));
};
cout << "x: " << x << endl << "V(x): " <<
splot(gauss, lorentz, x, sigma, gamma) << endl;`

正如@Christophe指出的那样,lambda 将具有不同的类型,而另一方面,这可以确保您为所有方法保持相同的类型。

有什么问题?

如果我很好地理解您的问题,当您定义splot()时,对传递的每个 lambda 使用不同的模板参数,它可以完美编译:

template <class F1, class F2>
double splot(F1 g, F2 l, double x, double sigma, double gamma) {
...
}

但是您使用的两个 lambda 具有相同的签名(相同的参数类型和相同的返回类型),因此您希望它们属于相同的类型,并且还需要编译以下splot()定义:

template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {
...
}

但是它不能编译,编译器甚至会混淆一条错误消息,该错误消息表明两个 lambda 具有不同的类型,而显示的类型名称指示相同的类型:

note:   template argument deduction/substitution failed:
note:   deduced conflicting types for parameter ‘F’ (‘main()::<lambda(double, double)>’ and ‘main()::<lambda(double, double)>’) 

为什么会有问题?

尽管存在误导性的错误消息,编译器是正确的。F的类型推导中存在错误:C++标准在[expr.prim.lambda.closure]/1中指出:

lambda 表达式的类型(也是闭包的类型) object)是唯一的、未命名的非联合类类型,称为闭包 类型,其属性如下所述。

因此,每个 lambda 都有不同的类型,即使它们共享相同的签名。

这基本上是因为它们不属于同一类型。

#include <iostream>
#include <cmath>
using namespace std;
int main (void) {
auto gauss = [](double x, double sigma)->double {
return exp(-1*x*x / (2*sigma*sigma)) / (sigma * sqrt(2*M_PI));
};
auto lorentz = [](double x, double gamma)->double {
return gamma / (M_PI*(x*x + gamma*gamma));
};
cout << typeid(gauss).name() << endl;
cout << typeid(lorentz).name() << endl;
return 0;
}

看看它如何为每个函数提供不同的 ID。在此链接中几乎没有解释。然后我想你必须坚持做:

template <class F, class G>
double splot(F g, G l, double x, double sigma, double gamma) {
auto s = [g, l, x, sigma, gamma](double x_prime)->double {
return g(x_prime, sigma) * l(x - x_prime, gamma);
};
return trapezoid(s, MIN, MAX, EPS);
}

或者你应该像Compare一样制作模板,或者使用类binary_function。请参阅示例。

原因是每个 lambda 都是它自己的、不同的类型,并且您的函数splot()要求gl是完全相同的类型。如果您将相同的 lambda 传递给splot(),您会注意到它会编译正常:

cout << "x: " << x << endl << "V(x): " <<
splot(gauss, gauss, x, sigma, gamma) << endl;

但是你需要模板吗?您只需要确保前两个参数是所需类型的函数,并且您已经知道该类型,因此请尝试以下操作:

double splot(std::function<double(double, double)> g, std::function<double(double, double)> l, double x, double sigma, double gamma) {
...
}