仅可移动对象能否封装为shared_ptr?ThreadPool代码疑问
问题1:仅可移动对象能否被封装为shared_ptr?
完全可以。std::shared_ptr的核心作用是管理堆上对象的生命周期,和对象本身是否支持拷贝没有关系。std::make_shared在创建shared_ptr时,会直接在堆内存上构造目标对象——对于仅可移动的类型,只要构造过程采用合法的初始化方式(比如移动构造、原地构造)就符合规则。
以你提到的ThreadPool代码为例,它通过std::make_shared<std::packaged_task<Result()>>(std::move(task))把传入的packaged_task移动到堆上,既满足了std::packaged_task仅可移动的特性,又完成了shared_ptr的封装。
问题2:lambda按值捕获task使shared_ptr引用计数变为2,是否意味着std::packaged_task被共享?你的理解是否正确?
你的理解完全正确。当lambda按值捕获shared_ptr<std::packaged_task<...>>时,会触发shared_ptr的拷贝构造,此时引用计数会从1增加到2。这意味着堆上的std::packaged_task对象被两个shared_ptr实例共享:一个是enqueue函数中的原task,另一个是lambda内部的捕获副本。
这种共享是线程安全且符合设计逻辑的:enqueue函数执行完毕后,原shared_ptr会被销毁,引用计数回到1;直到线程池中的工作线程取出并执行lambda时,lambda内的shared_ptr会继续管理packaged_task的生命周期,任务执行完成后,lambda被销毁,引用计数归零,packaged_task才会被正确释放。这种设计刚好避免了packaged_task在任务被执行前被提前销毁的问题。
内容的提问来源于stack exchange,提问作者f1msch

