使用std::thread时智能指针引发的编译错误原因咨询
C++ std::thread编译错误及解决方案解析
问题背景
开发使用std::thread的C++程序时遇到编译错误,自行修改代码后问题解决,但对原因存在疑问,以下是简化后的代码及问题说明:
初始报错代码
void main_func() { std::map<int, int>* requests_map = {}; SortInput(requests_map); // 该函数会修改requests_map的值,确认不会引发编译错误 Execute(requests_map, input_tensors); // input_tensors来自其他地方,确认不会引发编译错误 } // Execute函数实现(简化版): void Execute(std::map<int, int>* requests_info, std::shared_ptr<std::unordered_map<std::string, Tensor>> input_tensors) { // 在此创建智能指针 auto requests_info_ptr = std::make_shared<std::map<int, int>>(*requests_info); // 创建线程执行forward函数 my_thread = std::thread(ThreadForward, &input_tensors, &requests_info_ptr); my_thread.join(); } // ThreadForward函数实现(简化版): int ThreadForward(std::shared_ptr<std::unordered_map<std::string, Tensor>>* input_tensors, std::shared_ptr<std::map<int, int>>* requests_info) { // 处理逻辑 }
编译报错信息:
error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues 120 | typename decay<_Args>::type...>::value, | ^~~~~
修改后可行的代码
将智能指针的初始化移到Execute函数外,修改后代码如下:
void main_func() { // 在此初始化智能指针 auto requests_map = std::make_shared<std::map<int, int>>(); SortInput(requests_map); Execute(requests_map, input_tensors); // input_tensors来自其他地方,确认不会引发编译错误 } // Execute函数实现(简化版): void Execute(std::shared_ptr<std::map<int, int>> requests_info, std::shared_ptr<std::unordered_map<std::string, Tensor>> input_tensors) { // 无需额外操作,已通过参数获取shared_ptr // 创建线程执行forward函数,直接传入指针 my_thread = std::thread(ThreadForward, &input_tensors, &requests_info); my_thread.join(); } // ThreadForward函数实现(简化版): int ThreadForward(std::shared_ptr<std::unordered_map<std::string, Tensor>>* input_tensors, std::shared_ptr<std::map<int, int>>* requests_info) { // 处理逻辑 }
修改后编译通过,问题解决。
疑问解答
1. 为何初始写法会触发静态断言错误?
std::thread构造时会对传入参数做右值转换(通过std::decay处理),并将处理后的参数存储到线程内部存储区。初始写法中,requests_info_ptr是Execute内的局部变量,传入的&requests_info_ptr指向的是即将随函数执行结束销毁的对象——C++标准通过静态断言禁止这种明显的悬空指针风险。
同时,input_tensors是Execute的函数参数,传入&input_tensors本质是指向函数参数的指针,虽然参数生命周期在Execute内,但std::thread的参数检查逻辑会判定这种指向局部对象(含函数参数)的指针存在生命周期不匹配风险,最终触发断言。
2. 为何将智能指针初始化移到外部就能解决问题?
修改后,requests_map是main_func的局部变量,Execute接收的是shared_ptr的副本,requests_info作为Execute的参数,其生命周期覆盖整个Execute执行过程(包括my_thread.join()等待线程结束的阶段)。此时传入&requests_info,指向的对象在线程执行期间始终有效,编译器的静态断言检查可以确认安全性,因此不会报错。
3. 原始写法中使用裸指针转智能指针的方式存在什么问题?
原始写法存在三个核心问题:
- 冗余拷贝:
std::make_shared<std::map<int, int>>(*requests_info)会创建原map的完整拷贝,而非共享原对象所有权,带来不必要的内存和时间开销。 - 生命周期失控:原
requests_map是裸指针,若main_func未正确释放会引发内存泄漏;若提前释放,Execute中拷贝原map时会出现悬空指针访问。 - 逻辑偏离预期:
SortInput修改的是原map,但线程处理的是拷贝后的新map,两者数据完全独立,违背了“共享请求信息”的业务逻辑预期。
内容的提问来源于stack exchange,提问作者SamuraiBUPT
相关产品推荐
相关产品推荐

