De lo que puedo encontrar (a juzgar por una lectura superficial de la fuente no es precisamente sencillo y algunos experimentos) se copia el objeto clonado cada vez. Puede ser innecesario en caso de que la función tome su argumento mediante const &, pero en general el objeto puede ser mutado por la función. Si el objeto es caro de copiar, ¿no tendría sentido capturarlo por referencia (boost::ref
o boost::cref
viene a la mente) o, si el objeto original no existe en el punto de invocación, capturar un boost::shared_ptr
y escribir un adaptador método, que desempaqueta el smartpointer y llama al someFunction
?
Editar: A partir de la experimentación no solo se copiará construir ese objeto siempre que se copie boost::function
, sino que también se copiará varias veces dentro de boost::bind
. Probé usando el siguiente código usando impulso 1.45 bajo MinGW 32 con gcc 4.6 y -O2 (y -std = C++ 0x):
struct foo_bar {
std::vector<int> data; //possibly expensive to copy
foo_bar()
{ std::cout<<"default foo_bar "<<std::endl; }
foo_bar(const foo_bar& b):data(b.data)
{ std::cout<<"copy foo_bar "<<&b<<" to "<<this<<std::endl; }
foo_bar& operator=(const foo_bar& b) {
this->data = b.data;
std::cout<<"asign foo_bar "<<&b<<" to "<<this<<std::endl;
return *this;
}
~foo_bar(){}
};
void func(const foo_bar& bar) { std::cout<<"func"<<std::endl;}
int main(int, char*[]) {
foo_bar fb;
boost::function<void()> f1(boost::bind(func, fb));
std::cout<<"Bind finished"<<std::endl;
boost::function<void()> f2(f1);
std::cout<<"copy finished"<<std::endl;
f1();
f2();
return 0;
}
La salida resultante es como sigue:
default foo_bar
copy foo_bar 0x28ff00 to 0x28ff10
copy foo_bar 0x28ff10 to 0x28ff28
copy foo_bar 0x28ff28 to 0x28ff1c
copy foo_bar 0x28ff1c to 0x28ff34
copy foo_bar 0x28ff34 to 0x28fed4
copy foo_bar 0x28fed4 to 0x28fee4
copy foo_bar 0x28fee4 to 0x28fef4
copy foo_bar 0x28fef4 to 0x28fe14
copy foo_bar 0x28fe14 to 0x28fe24
copy foo_bar 0x28fe24 to 0x28fe34
copy foo_bar 0x28fe34 to 0x6a2c7c
Bind finished
copy foo_bar 0x6a2c7c to 0x6a2c94
copy finished
func
func
Entonces se solicitó al constructor de copias que creara f2 una vez y 11 veces para el enlace y la asignación a f1. Dado que el primer objeto se crea en la pila y las direcciones de las copias están muy cerca de eso y aumentando ligeramente, parece que el proceso de enlace pasa por muchas funciones, que el compilador no alinea en este caso y que cada una pasar el objeto por valor Utilizando sólo boost::bind
sin guardar el resultado en cualquier lugar:
int main(int, char*[]) {
foo_bar fb;
boost::function<void()> f1(boost::bind(func, fb));
return 0;
}
default foo_bar
copy foo_bar 0x28ff00 to 0x28ff10
copy foo_bar 0x28ff10 to 0x28ff28
copy foo_bar 0x28ff28 to 0x28ff1c
copy foo_bar 0x28ff1c to 0x28ff34
copy foo_bar 0x28ff34 to 0x28fef4
Así cinco copias sólo para vincular el objeto. Así que, en general, evitaría capturar cualquier cosa que tenga al menos costos de copia moderados por valor en cualquier parte del código que sea remotamente sensible al rendimiento. En GCCS comparación std::tr1::bind
y std::bind
realizar mucho mejor (en conjunción con std :: TR1 :: función/std :: función) (código es básicamente idéntica a la primera testcode, simplemente sustituya boost::
con std::tr1::
respectivamente std::
:
std::tr1::bind with std::tr1::function:
default foo_bar
copy foo_bar 0x28ff10 to 0x28ff28
copy foo_bar 0x28ff28 to 0x28ff34
copy foo_bar 0x28ff34 to 0x28ff04
copy foo_bar 0x28ff04 to 0x652c7c
Bind finished
copy foo_bar 0x652c7c to 0x652c94
copy finished
func
func
std::bind with std::function:
default foo_bar
copy foo_bar 0x28ff34 to 0x28ff28
copy foo_bar 0x28ff28 to 0x3c2c7c
Bind finished
copy foo_bar 0x3c2c7c to 0x3c2c94
copy finished
func
func
Asumo que std::bind
pasa por const ref para invocaciones internas, o está escrito de una manera que es más amigable con gccs inline para alinear algunos y eliminar constructores de copia redundantes. tr1::bind
aún se optimiza mejor que boost::bind
, pero aún está lejos de ser óptimo.
Por supuesto como siempre con tal tipo de pruebas YMMV con diferentes banderas de compilación/compiladores
escribí un programa de prueba y parece que el constructor copia del objeto almacenado se consiga llamar cada vez que se copia el objeto de función . Además, boost :: bind llama al constructor de copia 11 veces. – Chris
@Chris: Ok, entonces mi prueba no fue una casualidad, es bueno saberlo. Entonces, si uno puede usar C++ 11, parece que std :: bind es el camino a seguir por un margen lejano (aunque personalmente yo solo usaría lambdas en su lugar). – Grizzly