【问题标题】:How to pass two lambda functions using a single template parameter in C++如何在 C++ 中使用单个模板参数传递两个 lambda 函数
【发布时间】:2019-08-28 19:18:03
【问题描述】:

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

当我使用两个模板参数时,比如F1F2,它就像一个魅力。但是只要只有一个,g++就会抛出一个错误。立即将 lambdas 作为 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;
}

【问题讨论】:

标签: c++ templates lambda types functional-programming


【解决方案1】:

有什么问题?

如果我理解你的问题,当你定义 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) {
    ...
}

但它不能编译,而且编译器甚至使它与 an error message 混淆,这表明两个 lambda 具有不同的类型,而显示的类型名称表示相同的类型:

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

为什么会出现问题?

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

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

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

【讨论】:

  • 谢谢,我被误导了,因为我们的老师把这个作为家庭作业,他只用一个模板类编写了函数原型。
【解决方案2】:

我建议你使用std::function&lt;&gt;,如下所示:

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 将有不同的类型,而另一方面,这可确保您为所有方法保持相同的类型。

【讨论】:

  • 不错的一个!我只是建议使用更现代的 typedef 替代方案:using func = std::function&lt;double(double,double)&gt;;
  • 是的!我总是忘记这个选择,要习惯它:)
【解决方案3】:

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

#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。 this link 中几乎没有解释。那我猜你必须坚持做:

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 类。 See an example.

【讨论】:

  • 良好的诊断。但也有一些运气,因为name() 上没有任何保证,正如here 解释的那样:不同的编译器可以为不同的类型生成相同的name()。您的演示将与 gcc 错误消息一样具有误导性。
【解决方案4】:

原因是每个 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) {
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 2014-11-29
    • 2011-10-03
    • 1970-01-01
    • 2013-03-29
    • 1970-01-01
    相关资源
    最近更新 更多