最近在写代码的时候碰到了如下的问题,下面的例子可能看上去比较蠢,但足以说明我想表达的问题,我们有一个函数回调,用类型std::function<void>来表示,此时我们有一个类成员接收move-only的参数,比如unique_ptr,此时使用bind绑定出现错误
class Foo{
public:
void dothings(unique_ptr<int> uptr)
{
cout << "dothings" << endl;
}
};
int main()
{
auto uptr = std::make_unique<int>(1);
Foo foo;
std::function<void> f = std::bind(&Foo::dothings, std::move(uptr));
f();
}
这里导致失败的主要原因是std::function 需要一个 CopyConstructible Callable target,这里我们使用的std::bind虽然能隐式转换为一个std::function对象,然而由于bind参数出现了move only的对象,因此std::bind返回的functor也是move-only,不满足std::function所需要的CopyConstructible 条件,因此虽然std::bind这里不会触发copy操作,但是在其它某个地方,准确的说就是std::function在构造的时候,会拷贝形参的functor,当然也会拷贝其底层保存的参数,从而触发了对unique_ptr的拷贝,这是不允许的,编译出错。
解法方案:
使用 lambda 代替bind,并用引用捕获unique_ptr
1 auto func = [this = &foo, &uptr]()
2 {
3 this->dothings(std::move(uptr));
4 };
5 func(); // Synchronous call, fine
5 otherthread->runAtThatThread(func); // Asynchronous call, may be problematic
但是特别注意,这种方式实现时uptr的生命周期问题,2,3,4行代码并没有将uptr的对象生命周期交给func管理,也就是说如果在func调用之前uptr已经被释放,此时std::move(uptr)会对已经析构的uptr进行操作,非常危险
如果你确实需要使用unique_ptr,并想要将对象的生命周期交给lambda管理,对不起,目前没有很好的解决方法,也许你只能退而求其次,使用shared_ptr,它支持std::function所要求的CopyConstructible ,配合std::bind也能很好的工作
参考Stack Overflow上的讨论【传送】 ttps://stackoverflow.com/questions/20268482/binding-functions-with-unique-ptr-arguments-to-stdfunctionvoid