Search code examples
c++boostboost-bind

Why does boost::bind store arguments of the type passed in rather than of the type expected by the function?


I recently ran into a bug in my code when using boost::bind.

From the boost::bind docs:

The arguments that bind takes are copied and held internally by the returned function object.

I had assumed that the type of the copy that was being held was based on the signature of the function. However, it is actually based on the type of the value passed in.

In my case an implicit conversion was happening to convert the type used in the bind expression to the type received by the function. I was expecting this conversion to happen at the site of the bind, however it happens when the resulting function object is used.

In retrospect I should have been able to figure this out from the fact that using boost::bind gives errors when types are not compatible only at the call site, not the bind site.

My question is: Why does boost::bind work this way?

  1. It seems to give worse compiler error messages
  2. It seems to be less efficient when implicit conversion happens and there are multiple calls to the functor

But given how well Boost is designed I'm guessing there is a reason. Was it behavior inherited from std::bind1st/bind2nd? Is there a subtle reason why this would be hard/impossible to implement? Something else entirely?

To test that second theory I wrote up a little code snippet that seems to work, but there may well be features of bind I haven't accounted for since it's just a fragment:

namespace b = boost;
template<class R, class B1, class A1>
   b::_bi::bind_t<R, R (*) (B1), typename b::_bi::list_av_1<B1>::type>
   mybind(R (*f) (B1), A1 a1)
{
   typedef R (*F) (B1);
   typedef typename b::_bi::list_av_1<B1>::type list_type;
   return b::_bi::bind_t<R, F, list_type> (f, list_type(B1(a1)));
}

struct Convertible
{
   Convertible(int a) : b(a) {}
   int b;
};

int foo(Convertible bar)
{
   return 2+bar.b;
}

void mainFunc()
{
   int x = 3;
   b::function<int()> funcObj = mybind(foo, x);
   printf("val: %d\n", funcObj());
}

Solution

  • There are different cases where you need the arguments to be processed at the call site.

    The first such example is calling a member function, where you can either have the member called on a copy of the object (boost::bind( &std::vector<int>::push_back, myvector)) which most probably you don't want, or else you need to pass a pointer and the binder will dereference the pointer as needed (boost::bind( &std::vector<int>::push_back, &myvector )) --Note both options can make sense in different programs

    Another important use case is passing an argument by reference to a function. bind will copy performing the equivalent to a pass-by-value call. The library offers the option of wrapping arguments through the helper functions ref and cref, both of which store a pointer to the actual object to be passed, and at the place of call they dereference the pointer (through an implicit conversion). If the conversion to the target type was performed at bind time, then this would be impossible to implement.