将成员函数指针传递给无类函数

时间:2012-01-13 14:58:53

标签: c++ argument-passing member-function-pointers

在下面的代码中,我无法找到将成员函数传递给通用的根寻找器的方法。

#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变量?只是为了说清楚:我正在对二维函数进行一维根查找,但修复了两个参数中的一个。

3 个答案:

答案 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));