Reputation: 147
I am writing a program that calculates numerically the value of the voigt distribution. However I'm encountering a problem when I try to pass gauss' and lorentz's functions using a single class template parameter F
, even though they are of the same type.
When I use two templates arguments, say F1
and F2
, it works like a charm. But g++ throws an error whenever there is just one. Passing the lambdas immediately as splot
's (convolution's) parameters doesn't help.
#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;
}
Upvotes: 2
Views: 326
Reputation: 73366
If I understand well your question, when you define splot()
, using a different template parameter for each lambda passed, it compiles perfectly:
template <class F1, class F2>
double splot(F1 g, F2 l, double x, double sigma, double gamma) {
...
}
But both lambdas that you use have the same signature (same argument types and same return type) so you expect them to be of the same type, and the following definition of splot()
to compile as well:
template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {
...
}
But it doesn't compile, and the compiler makes it even confusing with an error message that suggests that both lambdas have a different type, whereas the displayed type name indicates the same type:
note: template argument deduction/substitution failed:
note: deduced conflicting types for parameter ‘F’ (‘main()::<lambda(double, double)>’ and ‘main()::<lambda(double, double)>’)
The compiler is right despite the misleading error message. There is an error in the type deduction with F
: the C++ standard states in [expr.prim.lambda.closure]/1
that:
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.
So every lambda has a different type, even if they share the same signature.
Upvotes: 3
Reputation: 7973
The reason is that each lambda is its own, distinct type, and your function splot()
requires g
and l
to be exactly the same type. If you pass the same lambda to splot()
, you will notice it will compile fine:
cout << "x: " << x << endl << "V(x): " <<
splot(gauss, gauss, x, sigma, gamma) << endl;
But do you need templates at all? You just need to make sure the first two arguments are functions of the required type, and you know the type already, so try this instead:
double splot(std::function<double(double, double)> g, std::function<double(double, double)> l, double x, double sigma, double gamma) {
...
}
Upvotes: 2
Reputation: 606
It's basically because they're not of the same type.
#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;
}
Look at how it gives diferent ids for each function. It's barely explained in this link. Then I guess you'll have to stick with doing:
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);
}
Or you should make templates just like Compare
or maybe use the class binary_function
. See an example.
Upvotes: 2
Reputation: 410
I'd advise you to use a std::function<>
as shown below:
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;`
as @Christophe pointed out, lambda will have different types, when this on the other hand, ensures that you keep the same type for all your methods.
Upvotes: 4