std::bind绑定unique_ptr
问题的来源,绑定和unique_ptr
std::bind
绑定unique_ptr
的时候生成的类型并非std::function
,而是一个不可拷贝的类型,这跟unique_ptr
的特性有关,这意味着如果需要暂时保存绑定的函数,没有能够接受对象的类型声明
如果不需要保存可以使用auto来接受绑定的对象,然后调用
void Test(int val)
{
}
std::unique_ptr<int> up = std::make_unique<int>();
// 错误的,无法通过编译
// std::function<void()> func = std::bind(&Test, std::move(up));
// 正确的,可以通过编译
auto func = std::bind(&Test, std::move(up));
其实这是绑定生成了一个不可拷贝(只可移动)的闭包类型,而std::function
是可以拷贝的因此类型不匹配(就算在bind外面套一个std::move也不会有作用)
std::_Binder<std::_Unforced,void (__cdecl*)(int),std::unique_ptr<int,std::default_delete<int>>>
其实绑定后的函数是一个内部类型
用模板
template<typename F>
class Leno
{
public:
Leno(F& func) : task_(std::move(func)) { }
void Run() { task_(); }
private:
F task_;
};
用模板可以保存这种类型的闭包对象,这样就可以在需要的时候再使用了,但是有一个问题,就是这种模板,会要求Leno对象在使用时提供模板参数,这又回到了一开始的问题,这个类型是内部类型,无法提供,那么Leno对象就无法构造。
外部无感知
那就需要对外无感知,在外部想要保存这么一个闭包对象时,完全不需要提供这个闭包对象的类型,需要利用继承和模板
class LenoBase
{
public:
virtual void Run() { }
};
template<typename F>
class Leno : public LenoBase
{
public:
Leno(F& func) : task_(std::move(func)) { }
void Run() override { task_(); }
private:
F task_;
};
class LenoCallback
{
public:
LenoCallback(std::shared_ptr<LenoBase> bs) : b_(bs) {}
void Run() { b_->Run(); }
private:
std::shared_ptr<LenoBase> b_;
};
template<typename F>
LenoCallback Get(F& func)
{
return LenoCallback(std::make_shared<Leno<F>>(func));
}
template<typename F>
std::unique_ptr<LenoCallback> GetUnique(F& func)
{
return std::make_unique<LenoCallback>(std::make_shared<Leno<F>>(func));
}
int main() {
std::unique_ptr<int> test = std::make_unique<int>(100);
//auto func([pw = std::move(test)]() {
// cout << *pw << endl;
// });
auto func = std::bind(&hahahha, std::move(test));
function<void()> func1 = std::bind(&lalala, 10);
std::unique_ptr<LenoCallback> callback = GetUnique(func);
callback->Run();
return 0;
}
- 基础类型LenoBase是不需要模板参数的,因此,在对外开放的类型LenoCallback中有该类型的指针,这时,LenoCallback就不需要模板参数;
- 需要模板参数的是Leno实现类,在函数中创建该类对象,只需要函数模板,并且利用多态实现父类指针调用子类方法;
- LenoBase基础类需要提供一定的虚函数实现接口;
到这里就完成了绑定unique_ptr
闭包的保存,并且对外的Get
和GetUnique
函数也不需要提供类型