线程函数与普通函数中shared_ptr转weak_ptr的行为差异问题
这个问题我之前也碰到过,本质是std::thread构造函数的参数处理逻辑和普通函数调用不一样导致的,咱们一步步拆解:
为什么线程调用会出现这个异常?
当你把shared_ptr<int>直接传给期望weak_ptr<int>的线程函数时,std::thread的构造机制会先**复制一份完整的shared_ptr**来暂存这个参数——因为线程需要把参数传递到新的执行上下文,它无法直接在构造阶段完成shared_ptr到weak_ptr的隐式转换。这个临时的shared_ptr副本会让原对象的引用计数上升,直到新线程启动后,才会把这个副本转换成weak_ptr传递给目标函数,之后临时副本销毁,引用计数才会降回来。
而普通函数调用时,参数的隐式转换(shared_ptr转weak_ptr)是在调用者线程中直接完成的,这个转换过程只会创建weak_ptr,不会产生额外的shared_ptr副本,所以引用计数完全不会变化。
解决办法
有两种简单可靠的方式可以避免这个问题:
1. 显式转换为weak_ptr后再传递
在构造std::thread时,主动把shared_ptr转换成weak_ptr,让线程直接复制weak_ptr而非shared_ptr:
std::shared_ptr<int> sp = std::make_shared<int>(42); // 显式构造weak_ptr传入 std::thread t(thrdfn, std::weak_ptr<int>(sp)); // C++17及以上可以省略模板参数,更简洁 std::thread t(thrdfn, std::weak_ptr(sp));
weak_ptr的复制操作不会影响shared_ptr的引用计数,完全符合你的需求。
2. 使用lambda表达式封装调用
用lambda来包装线程函数的调用,在lambda内部完成shared_ptr到weak_ptr的转换,这样线程传递的是lambda对象,不会额外复制shared_ptr:
std::shared_ptr<int> sp = std::make_shared<int>(42); // 用初始化捕获直接构造weak_ptr,避免复制shared_ptr std::thread t([wp = std::weak_ptr(sp)]() { thrdfn(wp); });
这种方式代码更整洁,也能确保不会意外提升引用计数。
验证效果
不管用哪种方式,你都可以在传递前后打印sp.use_count()来验证:传递前是1,传递后依然是1,不会因为线程的创建而上升。
内容的提问来源于stack exchange,提问作者Daksh Gupta

