Errors like "is not a class, struct, or union type" when using boost::bind

Decoding compile-time errors when using complex template libraries (like Boost) can be a bit involved with the current generation of c++ compilers. Hopefully this will improve as the compilers are improved but in the meantime it is useful to keep track of the type of errors that you get and why. Here is an example of one of those non-obvious errors.

The problem

If you try to compile the following code:

double f(double x, double y, double z)
{
  return 1.0;
}

int main(void)
{
  // following line won't compile:
  boost::function<double (double x)> mf=boost::bind(f,_1, 3.0);
}

with g++ 4.3.2, you will get the following handful:

/usr/include/boost/bind.hpp: In instantiation of ‘boost::_bi::result_traits<boost::_bi::unspecified, double (*)(double, double, double)>’:
/usr/include/boost/bind/bind_template.hpp:15:   instantiated from ‘boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >’
t1.cpp:12:   instantiated from here
/usr/include/boost/bind.hpp:66: error: ‘double (*)(double, double, double)’ is not a class, struct, or union type
/usr/include/boost/function/function_template.hpp: In static member function ‘static R boost::detail::function::function_obj_invoker1<FunctionObj, R, T0>::invoke(boost::detail::function::function_buffer&, T0) [with FunctionObj = boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >, R = double, T0 = double]’:
/usr/include/boost/function/function_template.hpp:787:   instantiated from ‘void boost::function1<R, T0, Allocator>::assign_to(const Functor&) [with Functor = boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >, R = double, T0 = double, Allocator = std::allocator<void>]’
/usr/include/boost/function/function_template.hpp:624:   instantiated from ‘boost::function1<R, T0, Allocator>::function1(Functor, typename boost::enable_if_c<boost::type_traits::ice_not::value, int>::type) [with Functor = boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >, R = double, T0 = double, Allocator = std::allocator<void>]’
/usr/include/boost/function/function_template.hpp:886:   instantiated from ‘boost::function<R ()(T0), Allocator>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not::value, int>::type) [with Functor = boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >, R = double, T0 = double, Allocator = std::allocator<void>]’
t1.cpp:12:   instantiated from here
/usr/include/boost/function/function_template.hpp:137: error: no match for call to ‘(boost::_bi::bind_t<boost::_bi::unspecified, double (*)(double, double, double), boost::_bi::list2<boost::arg<1> (*)(), boost::_bi::value<double> > >) (double&)’
make: *** [t1] Error 1

Diagnosis

The line with the best clue is probably the following:

/usr/include/boost/bind.hpp:66: error: ‘double (*)(double, double, double)’ is not a class, struct, or union type

which prints the signature of the function f that we are trying to bind. What it really is telling you that the function object bind created didn't match this signature, and the reason in this case is simply that we provided too few arguments when binding:

boost::function<double (double x)> mf=boost::bind(f,_1, 3.0);

This line deals with only two arguments of f, i.e., it is equivalent to f(_1,3.0), but f takes three arguments, i.e., it needs to be called as f(1.0, 2.0, 3.0).

Solution

The solution of course is to check that you are providing the right number of arguments for the function you are trying to bind. In this case:

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

double f(double x, double y, double z)
{
  return 1.0;
}

int main(void)
{
  // following line won't compile:
  boost::function<double (double x)> mf=boost::bind(f,_1, 3.0);
  // following line is OK
  boost::function<double (double x)> mf2=boost::bind(f,_1, 3.0, 3.0);
}