Я пишу программу, которая численно вычисляет значение распределения Фойгта. Однако я сталкиваюсь с проблемой, когда пытаюсь передать функции Гаусса и Лоренца, используя один параметр шаблона класса 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;
}
Если я хорошо понимаю ваш вопрос, когда вы определяете 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
, что:
The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.
Таким образом, каждая лямбда имеет свой тип, даже если они имеют одинаковую сигнатуру.
Спасибо, я был введен в заблуждение, потому что наш учитель дал нам это в качестве домашнего задания, и он написал прототип функции только с одним классом шаблона.
Я бы посоветовал вам использовать 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, лямбда будет иметь разные типы, когда это, с другой стороны, гарантирует, что вы сохраните один и тот же тип для всех своих методов.
Хороший ! Я бы просто предложил более современную альтернативу typedef: using func = std::function<double(double,double)>;
Ага! Я всегда забываю об этой альтернативе, нужно привыкнуть к ней :)
Это в основном потому, что они не одного типа.
#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
. См. пример.
Хорошая диагностика. Но также и некоторая удача, потому что нет никаких гарантий на name()
, как объяснено здесь: другой компилятор может создать одно и то же name()
для разных типов. Тогда ваша демонстрация будет столь же вводящей в заблуждение, как и сообщение об ошибке gcc.
Причина в том, что каждая лямбда имеет свой собственный тип, и ваша функция 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) {
...
}