为什么这个boost绑定非静态成员函数失败?

时间:2014-02-25 14:05:55

标签: c++ boost boost-bind

为什么以下编译即将自由函数作为带有正确签名的参数传递:

inline double free_adapter_f(unsigned n, const double *x, double *grad, void *d) {
    return 0.0;
}

nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());
opt.set_min_objective(free_adapter_f, NULL);

而另一个不编译,即传递具有相同签名的boost::bind类成员函数的结果:

template<class Space, class Solution, class Oracle>
inline double NelderMead<Space, Solution, Oracle>::adapter_f(unsigned n, const double *x, double *grad, void *d) {
    return 0.0;
}

nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());      
opt.set_min_objective(boost::bind(&NelderMead::adapter_f, this, ::_1, ::_2, ::_3, ::_4), NULL);

错误消息如下:

nelder_mead.h(98): error: no instance of overloaded function "nlopt::opt::set_min_objective" matches the argument list
    argument types are: (boost::_bi::bind_t<double, boost::_mfi::mf4<double,     NelderMead<TestSpace, VectorXd, oracle_f>, unsigned int, const double *, double *, void *>, boost::_bi::list5<boost::_bi::value<NelderMead<TestSpace, VectorXd, oracle_f> *>, boost::arg<1>, boost::arg<2>, boost::arg<3>, boost::arg<4>>>, long)
        object type is: nlopt::opt
opt.set_min_objective(boost::bind(&NelderMead::adapter_f, this, ::_1, ::_2, ::_3, ::_4), NULL);

更新:重载的set_min_objective是:

 typedef double (*func)(unsigned n, const double *x, double *grad, void *f_data);
 typedef double (*vfunc)(const std::vector<double> &x, std::vector<double> &grad, void *f_data);
 void set_min_objective(func f, void *f_data);
 void set_min_objective(vfunc vf, void *f_data);

3 个答案:

答案 0 :(得分:3)

你需要定义set_min_objective,它接受boost :: function作为第一个参数:

 typedef boost::function<double (unsigned n, const double *x, double *grad, void *f_data)> func_t;
 ...
 void set_min_objective(func_t, void*);
 ...

另一件事 - 你最好不要使用NULL

答案 1 :(得分:2)

这是一个简单的示例,它演示了您的问题:

#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>

namespace g
{
  typedef int (*func)(int a, int b, int c);

  void bar(func f)
  {
    std::cout << "g::bar:: called" << (*f)(10, 20, 30) << std::endl;
  }

  // Disable the over load below and you will get the same error
  void bar(boost::function<int(int, int, int)> f)
  {
    std::cout << "g::bar:: called" << f(10, 20, 30) << std::endl;
  } 
}

template <typename A, typename B, typename C>
class foo
{

public:

  int bar(int a, int b, int c) const
  { return a + b + c; }

  void call()
  {
    g::bar(boost::bind(&foo::bar, this, ::_1, ::_2, ::_3));
  }
};

int main(void)
{
  foo<int, double, int> f;
  f.call();
  return 0;
}

主要原因是boost::function<>不是可转换到函数指针,所以你需要提供一个接受它的重载(如上所述。)

编辑:只是为了进一步澄清事情。 boost::bind() 显式返回boost::function<>,但是,它返回的对象可以存储在boost::function<>的正确实例中,在上述情况下,正确实例化是boost::function<int(int, int, int)>

通常情况下,如果您有兴趣传播它(没有模板)或存储它以供以后使用,您只需要将其存储在boost::function中。在这种情况下,当您传递bind()的结果时,您需要使用正确的重载来接受来自boost::bind()的返回对象,并且最简单的方法是在不使用模板的情况下执行此操作接受boost::function(如上所述。)

通常情况下,我是务实的,所以我会在可能的情况下诉诸于此(不知道你想对f做什么)。

template <typename F>
double set_min_objective(F f, ...)
{
}

然后你是不可知论者,当然纯粹主义者会有其他意见。

注意:boost::function<>的一个好处是你可以将一个非成员函数指针存储在一个中(只要签名匹配。)所以实际上你只需要一个接受你的函数的版本正确的boost::function<>,它将适用于两种情况(成员函数boost::bind()和非成员函数。)

EDIT2:好的,鉴于附加信息,您必须使用以下机制,您需要拥有类的非成员函数,然后将其委托给成员函数,例如:

<>
class NelderMead
{

static double delegate_f(unsigned n, const double *x, double *grad, void *f_data)
{
  // I'm assuming here the frame work passed you whatever you gave in f_data
  NelderMead* inst = reinterpret_cast<NelderMead*>(f_data);
  return inst->adapter_f(n, x, grad);
}

double adapter_f(unsigned n, const double *x, double *grad)
{
}

void set()
{
  nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());      
  opt.set_min_objective(delegate_f, this); //<-- here pass the instance as the additional data
}

};

这是第三方库采用的典型模式,与用户代码无关。

答案 2 :(得分:0)

set_min_objective的两个重载都期望指向函数的指针作为第一个参数,但boost::bind返回的对象不是指向函数的指针,它是一个函数对象。

boost::bind返回一个存储目标函数和任何绑定参数的函数对象,它不合成函数并返回指向它的指针,或者将指向成员函数的指针神奇地转换为指针-to-功能。那将是神奇的。