说我有一个这样的功能:
void someFunction(const ExpensiveObjectToCopy&);
如果我将boost::function制作出来,该函数将在其闭包中存储对象自己的克隆拷贝:
boost::function<void()> f = boost::bind(someFunction, x); // <-- f saves a copy of x
现在,如果我开始传递f,boost::function复制构造函数是否会再次复制该对象,还是每个函数共享相同的闭包? (即像这样)
boost::function<void()> f2 = f;
callSomeFunction(f);
etc.
最佳答案
根据我的发现(从对不完全简单的源的粗略阅读和一些实验来看),它每次都会复制克隆的对象。如果函数使用const&作为参数,则可能是不必要的,但通常对象可能会被该函数突变。如果对象的复制成本很高,那么通过引用捕获它(想到boost::ref
或boost::cref
)就没有意义,或者,如果原始对象在调用时不存在,则捕获一个boost::shared_ptr
并编写一个适配器方法,该方法可打开smartpointer的包装并调用someFunction
?
编辑:从实验中,无论何时复制boost::function
,它不仅会复制构造该对象,而且还会在boost::bind
内复制多次。我在mingw 32下使用带有gcc 4.6和-O2(和-std = c++ 0x)的boost 1.45使用以下代码进行了测试:
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;
}
结果输出如下:
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
因此,复制构造函数被要求一次创建f2和11次,以将其绑定(bind)和分配给f1。由于第一个对象是在堆栈上创建的,并且拷贝的地址与之非常接近并且略有增加,因此绑定(bind)过程似乎需要很多函数,在这种情况下编译器不会内联这些函数,并且每个函数按值传递对象。仅使用
boost::bind
而不将结果保存在任何地方: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
因此,仅需绑定(bind)五个拷贝即可绑定(bind)对象。因此,我通常会避免在代码的任何甚至远程性能敏感的部分中捕获每值至少具有中等复制成本的任何内容。相比之下,gcc的
std::tr1::bind
和std::bind
的性能要好得多(与std::tr1::function/std::function结合使用)(代码与第一个测试代码基本相同,只是分别用boost::
和std::tr1::
替换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
我假设
std::bind
是由const ref传递给内部调用的,或者是以对gccs inliner更加友好的方式编写的,以内联某些代码并消除了多余的拷贝构造函数。 tr1::bind
的优化程度要比boost::bind
更好,但仍远未达到最佳状态。当然,与使用不同编译标志/编译器的此类测试YMMV一样,
关于c++ - 复制boost::function也会复制闭包吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8688675/