在下面的代码中,我无法找到将成员函数传递给通用的根寻找器的方法。
#include <stdio.h>
double OneDimBisector(double (*fun)(float), float a, float b, float tol){
double val;
val = (*fun)(0.5*(b-a)); // actually: do proper bisection
return val;
}
class EOS {
public:
double S_array[10][10]; // actually: filled by constructor
double S(double T, double P);
double T_PS(double P, double S);
double functForT_PS(double T);
double (EOS::*pfunctForT_PS)(double);
double Sseek, Pseek;
};
double EOS::S(double T, double P){
double val = T+P; // actually: interpolate in S_array
return val;
}
double EOS::functForT_PS(double T){
return S(T,Pseek)-Sseek;
}
// Find T from P and S (T is invertible), assuming the intervals are ok
double EOS::T_PS(double P, double S0){
double Tmin = 2., Tmax = 7., T1, tol=1e-8;
pfunctForT_PS = &EOS::functForT_PS;
Sseek = S0;
Pseek = P;
printf("\n %f\n", (*this.*pfunctForT_PS)(4.)); // no problem
T1 = OneDimBisector(pfunctForT_PS, Tmin, Tmax, tol); // wrong type for pfunctForT_PS
return T1;
}
int main() {
double P=3., S=8;
EOS myEOS;
printf("\n %f %f %f\n",P,S,myEOS.T_PS(P,S));
}
我不想让root-finder成为成员,因为它不是特定于这个类,并且使所有static
的解决方案看起来非常不优雅。有人有想法吗?这一定是常见的情况,但我找不到一个对我来说也可以理解的相关帖子。
谢谢!
编辑:实际上,我还想问:除了我做的事情之外,是否有一种正确的,线程安全的方式来设置Pseek
变量?只是为了说清楚:我正在对二维函数进行一维根查找,但修复了两个参数中的一个。
答案 0 :(得分:3)
一种方法是更改根查找器的签名(添加#include <functional>
):
double OneDimBisector(std::function<double(float)> f, float a, float b, float tol);
然后使用bind
:
T1 = OneDimBisector(std::bind(pfunctForT_PS, this, std::placeholders::_1),
Tmin, Tmax, tol);
这带来了一定的开销。如果您不介意拥有大量重复代码,可以将该功能设为模板:
template <typename Func>
double OneDimBisector(Func f, float a, float b, float tol);
您以相同的方式调用它,但每次有新的函数类型时,都会在您的compilate中创建一个新的模板实例。
“传统”解决方案是拥有一个接受额外实例参数的自由(或静态)函数。
更新:“传统解决方案”:
double OneDimBisector(double(*f)(float, void *), void * data, ...);
double EOSBisect(float f, void * data)
{
EOS * e = static_cast<EOS *>(data); // very "traditional"
return e->functorForT_PS(f);
}
用法:T1 = OneDimBisector(EOSBisect, this, Tmin, Tmax, tol);
答案 1 :(得分:1)
您不能将成员函数指针作为函数指针传递,因为后者缺少正确调用成员函数指针的上下文指针(this
)。
解决此问题的一般方法(如在标准C ++库中)是使用模板:
template <typename F>
double OneDimBisector(F fun, float a, float b, float tol){
double val;
val = fun(0.5*(b-a));
return val;
}
并将函数对象传递给它
struct Evaluator
{
EOS* this_;
Evaluator(EOS* this_) : this_(this_) {} // constructor
double operator()(double value) const // call the function
{
return this_->functForT_PS(value);
}
};
T1 = OneDimBisector(Evaluator(this), Tmin, Tmax, tol);
你也可以使用std::bind1st(std::mem_fun(&EOS::functForT_PS), this)
,但它的作用与上面的结构相同。 (顺便说一下,std :: bind1st和std :: mem_fun都已被弃用。)
如果你不喜欢模板,你可以接受一个多态函数(例如在C ++ 11中使用Boost.Function或std :: function),但它会慢一点:
double OneDimBisector(const boost::function<double(double)>& fun,
float a, float b, float tol)
{
return fun(0.5 * (b-a));
}
最后,如果你可以使用C ++ 11,你可以在调用OneDimBisector时使用lambda函数:
T1 = OneDimBisector([=](double value){ return functForT_PS(value); },
Tmin, Tmax, tol);
答案 2 :(得分:0)
您遇到的问题是函数指针与成员函数指针不同。
一种解决问题的常见(Java World)方法是使用策略模式(Bisector的乐趣将是策略的一些实现)。
常见的C ++ - 方法将使用仿函数/绑定,例如加强:
typedef boost::function<double (double)> MyFun;
double OneDimBisector(const MyFun & fun, float a, float b, float tol){
double val;
val = fun(0.5*(b-a)); // actually: do proper bisection
return val;
}
// Calling
T1 = OneDimBisector (boost::bind (&EOS::functForT_PS, *this), Tmin, Tmax, tol));