I do some experiences about boost::bind
And I find out that boost::bind would incur heavy copy if you don't pass by referene

#include<iostream>

#include<boost/bind.hpp>

struct test_for_bind_00
{
  test_for_bind_00() {}
  test_for_bind_00(test_for_bind_00 const &Obj) { std::cout<<"this is copy constructor :: tfb00"<<std::endl; }
};

struct test_for_bind
{
  void tamaya() { std::cout<<"tamaya"<<std::endl; }
  void tamaya(test_for_bind_00 const&) { std::cout<<"this is copy :: tamaya"<<std::endl; }

  test_for_bind() {}

  test_for_bind(test_for_bind const &Obj)
  {
    std::cout<<"this is copy constructor :: tamaya"<<std::endl;
  }
};

struct F2
{

  typedef void result_type;
  void operator()( ) { std::cout<<"F2, F2, F2"<<std::endl; }
  F2() {}
  F2(F2 const&) { std::cout<<"this is copy :: F2"<<std::endl; }
};


int main()
{
  test_for_bind tb_0; 
    
  boost::bind( &test_for_bind::tamaya, test_for_bind() )();

  test_for_bind_00 tfb00_0;
  boost::bind( &test_for_bind::tamaya, &tb_0, boost::cref(tfb00_0) )();

  F2 f;
  boost::bind(f)
 
  return 0;
}
boost::bind( &test_for_bind::tamaya, test_for_bind() )();

would produce

this is copy constructor :: tamaya
this is copy constructor :: tamaya
this is copy constructor :: tamaya
this is copy constructor :: tamaya

boost::bind( &test_for_bind::tamaya, &tb_0, boost::cref(tfb00_0) )();

would produce

this is copy constructor :: tfb00
this is copy constructor :: tfb00
this is copy constructor :: tfb00
this is copy constructor :: tfb00
this is copy constructor :: tfb00
this is copy :: tamaya

boost::bind(f)();

would produce

this is copy :: F2
this is copy :: F2
this is copy :: F2
F2, F2, F2

I don't know why boost::bind would need to make so many copies
besides,

boost::bind( &test_for_bind::tamaya, &tb_0, boost::cref(tfb00_0) )();

would produce one more copy of "tb_0" no matter what.
How could I save the copy of "tb_0"?

Thanks a lot

Edited 5 Years Ago by stereomatching: n/a

You need the magic of Perfect Forwarding in order to completely avoid the unnecessary copies. If you use the C++11 versions of the bind() functions (in header <functional> ), you get much better performance.

First, I tried your code (with Boost.Bind):

test_for_bind tb_0; 
    
  boost::bind( &test_for_bind::tamaya1, test_for_bind() )();

  boost::bind( &test_for_bind::tamaya1, boost::ref(tb_0) )();

  test_for_bind_00 tfb00_0;
  boost::bind( &test_for_bind::tamaya2, &tb_0, boost::cref(tfb00_0) )();

  boost::bind( &test_for_bind::tamaya2, boost::ref(tb_0), boost::cref(tfb00_0) )();

  F2 f;
  boost::bind(f)();

Which gave me this output:

this is copy constructor :: tamaya
this is copy constructor :: tamaya
this is copy constructor :: tamaya
this is copy constructor :: tamaya
tamaya
tamaya
this is copy :: tamaya
this is copy :: tamaya
this is copy :: F2
this is copy :: F2
this is copy :: F2
F2, F2, F2

Which means that only the test_for_bind() and the f temporaries are copied several times, everything else is forwarded correctly. I don't why your implementation copies the const-ref parameter ( tfb00_0 ), it shouldn't. My boost version is 1.45.0

Now, if I use the C++0x version (with GCC 4.6.2), I tried this exact replica:

test_for_bind tb_0; 
    
  std::bind( &test_for_bind::tamaya1, test_for_bind() )();

  //boost::bind( &test_for_bind::tamaya, tb_0 )();
  std::bind( &test_for_bind::tamaya1, std::ref(tb_0) )();

  test_for_bind_00 tfb00_0;
  std::bind( &test_for_bind::tamaya2, &tb_0, std::cref(tfb00_0) )();

  std::bind( &test_for_bind::tamaya2, std::ref(tb_0), std::cref(tfb00_0) )();

  F2 f;
  boost::bind(f)();

And I got this output:

this is copy constructor :: tamaya
tamaya
tamaya
this is copy :: tamaya
this is copy :: tamaya
this is copy :: F2
this is copy :: F2
this is copy :: F2
F2, F2, F2

And if I try this:

boost::bind(std::ref(f))();

I simply get that:

F2, F2, F2

EDIT

I just realized I didn't really answer your question: "why boost::bind would need to make so many copies". The reason why it causes so many copies is obviously because the actual bind function template (and all its overloads) is only a front-end for a series of implementation classes that actual realize the binding of the arguments to the function. I would guess there are three levels of implementation classes plus one additional level for each argument to bind (which explains that a nullary functor bind requires 3 copies, a unary functor bind requires 4 copies and a binary functor bind requires 5 copies, as your results showed). Without perfect forwarding, it is difficult for the implementation to avoid copies across the layers of implementation of the argument binding classes, although using reference-wrappers should solve that problem as it does for me (but not in your implementation, for some unknown reason). Finally, the multiple copying of the functor (first argument to the bind function) is probably required somehow, and it is not very bad because the first argument (the actual function) will be either a function-pointer, a member-function-pointer, or a callable object. The first two are really cheap to copy and there is no point in carrying them by reference. The last version, the callable object, could possibly be more expensive to copy, but in general, they are not (e.g. it is specified in Boost and in almost all STL algorithms that functors (like comparators or predicates) should be really cheap to copy and that it could be copied multiple times during any given algorithm). So, functors should always be implemented to be cheap to copy and copyable an indefinite number of times, this is a wide-spread convention that Boost.Bind or std::bind obeys to.

Edited 5 Years Ago by mike_2000_17: n/a

Thanks a lot

(but not in your implementation, for some unknown reason)

It was my fault, the bind didn't copy at all

struct test_for_bind
{
void tamaya() { std::cout<<"tamaya"<<std::endl; }
//this is the problem, I should not define an ambiguity message like "this is a copy"
void tamaya(test_for_bind_00 const&) { std::cout<<"this is copy :: tamaya"<<std::endl; }
 
test_for_bind() {}
 
test_for_bind(test_for_bind const &Obj)
{
std::cout<<"this is copy constructor :: tamaya"<<std::endl;
}
};

This lesson tell me I have to define the error message carefully

This article has been dead for over six months. Start a new discussion instead.