Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::bind don't compile with member template function

Tags:

c++

I just write an simple example to test boost::bind. I use it to instantiate a template member function, but it won't compile with g++ 4.6.0. I don't know what's the problem. Here's the code:

#include <boost/bind.hpp>

struct Functor
{
  void operator()()
  {

  }
};

struct DerivedFinishAction
{
  DerivedFinishAction()
  {}

  void Inc()
  {

  }

  template <typename T>
  void TmplFunc(T t)
  {
    (boost::bind(&DerivedFinishAction::BindFunc<T>, this , t))();
  }

  template <typename T>
  void BindFunc(T t)
  {
    t();
  }

  void Func()
  {
    Functor f;
    TmplFunc(f); // this is OK
    TmplFunc(boost::bind(&DerivedFinishAction::Inc, this)); // compile error
  }
};

int main(int argc, char *argv[])
{

  return 0;
}

And g++ gives the following errors:

In file included from /usr/include/boost/bind.hpp:22:0,
                 from testBind.cpp:1:
/usr/include/boost/bind/bind.hpp: In member function ‘void boost::_bi::list2<A1, A2>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = boost::_mfi::mf1<void, DerivedFinishAction, boost::_bi::bind_t<void, boost::_mfi::mf0<void, DerivedFinishAction>, boost::_bi::list1<boost::_bi::value<DerivedFinishAction*> > > >, A = boost::_bi::list0, A1 = boost::_bi::value<DerivedFinishAction*>, A2 = boost::_bi::bind_t<void, boost::_mfi::mf0<void, DerivedFinishAction>, boost::_bi::list1<boost::_bi::value<DerivedFinishAction*> > >]’:
/usr/include/boost/bind/bind_template.hpp:20:59:   instantiated from ‘boost::_bi::bind_t<R, F, L>::result_type boost::_bi::bind_t<R, F, L>::operator()() [with R = void, F = boost::_mfi::mf1<void, DerivedFinishAction, boost::_bi::bind_t<void, boost::_mfi::mf0<void, DerivedFinishAction>, boost::_bi::list1<boost::_bi::value<DerivedFinishAction*> > > >, L = boost::_bi::list2<boost::_bi::value<DerivedFinishAction*>, boost::_bi::bind_t<void, boost::_mfi::mf0<void, DerivedFinishAction>, boost::_bi::list1<boost::_bi::value<DerivedFinishAction*> > > >, boost::_bi::bind_t<R, F, L>::result_type = void]’
testBind.cpp:24:5:   instantiated from ‘void DerivedFinishAction::TmplFunc(T) [with T = boost::_bi::bind_t<void, boost::_mfi::mf0<void, DerivedFinishAction>, boost::_bi::list1<boost::_bi::value<DerivedFinishAction*> > >]’
testBind.cpp:37:58:   instantiated from here
/usr/include/boost/bind/bind.hpp:313:9: error: invalid use of void expression

Can anybody help explain this? Why the first instantiation is OK while the second one causes compile error?

like image 862
airekans Avatar asked May 15 '12 02:05

airekans


1 Answers

There's a (non-obvious) feature of boost::bind involved here. http://www.boost.org/libs/bind/#nested_binds

If you write:

void func1(int len) {return len+1;};
int func2(std::string str) {return str.length();};

assert(
    boost::bind(func1, boost::bind(func2, _1) )("Hello")
    == 6 );

boost::bind assumes that what you meant is "run func2 on "Hello", then run func1 on the result". This allows more interesting partial function application.

In your program, you have an expression which amounts to:

boost::bind(&DerivedFinishAction::BindFunc<...>, 
            this, 
            boost::bind(&DerivedFinishAction::Inc, this))

So boost::bind tries to figure out how to run DerivedFinishAction::Inc on it arguments, so it can pass that result into DerivedFinishAction::BindFunc<...>. But DerivedFinishAction::Inc returns void, which cannot be passed into DerivedFinishAction::BindFunc<...>. Thus you get a compiler error:

/usr/include/boost/bind/bind.hpp:313:9: error: invalid use of void expression

Edit: Per the documentation, you can use protect to achieve your desired behavior:

#include <boost/bind/protect.hpp>
...
TmplFunc(boost::protect(boost::bind(&DerivedFinishedAction::Inc, this))); // no longer an error
...
like image 93
Managu Avatar answered Nov 25 '22 01:11

Managu