Как передать две лямбда-функции, используя один параметр шаблона в C ++ - PullRequest
2 голосов
/ 07 апреля 2019

Я пишу программу, которая рассчитывает численно значение распределения voigt.Однако я сталкиваюсь с проблемой, когда пытаюсь передать функции Гаусса и Лоренца, используя один параметр шаблона класса F, даже если они одного типа.

Когда я использую два аргумента шаблона, произнеситеF1 и F2, это работает как шарм.Но g ++ выдает ошибку, когда есть только одна.Незамедлительная передача лямбды как параметров 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;
}

Ответы [ 4 ]

4 голосов
/ 07 апреля 2019

Я бы посоветовал вам использовать 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, лямбда будет иметь разные типы, если, с другой стороны, это гарантирует, что вы сохраните один и тот же тип для всех ваших методов.

3 голосов
/ 07 апреля 2019

В чем проблема?

Если я хорошо понимаю ваш вопрос, когда вы определяете splot(), используя разные параметры шаблона для каждой пройденной лямбды, она отлично компилируется:

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

Но обе лямбды, которые вы используете, имеют одинаковую сигнатуру (одинаковые типы аргументов и одинаковый тип возвращаемого значения), поэтому вы ожидаете, что они будут одинакового типа, и для компиляции также будет использовано следующее определение splot():

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

Но он не компилируется, и компилятор даже путает его с сообщением об ошибке , которое предполагает, что обе лямбды имеют разный тип, тогда как отображаемое имя типа указывает на тот же тип:

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 утверждает, что:

Тип лямбда-выражения (который также являетсятип объекта замыкания) - это уникальный , неназванный тип класса без объединения, называемый типом замыкания, свойства которого описаны ниже.

Таким образом, каждая лямбда имеет свой тип, даже если они имеют одинаковую подпись.

2 голосов
/ 07 апреля 2019

Причина в том, что каждая лямбда имеет свой собственный, отдельный тип, и ваша функция splot() требует, чтобы g и l были абсолютно одинакового типа.Если вы передадите ту же самую лямбду на 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) {
    ...
}
2 голосов
/ 07 апреля 2019

Это в основном потому, что они не одного типа.

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

Посмотрите, как он дает разные идентификаторы для каждой функции. Это едва объясняется в этой ссылке . Тогда, я думаю, вам придется заняться:

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. См. Пример .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...