C++从unordered_map取std::thread报错:拷贝构造函数已删除
从unordered_map中取出std::thread的正确姿势
咱们先搞清楚你为啥会遇到这个错误:std::thread是完全不可拷贝的类型——它的拷贝构造函数被C++标准显式删除了,所以任何试图直接拷贝它的操作(比如你最初用return map_.at(handle);)都会触发这个“引用已删除函数”的错误。
你尝试用std::move的思路是对的,但可能没注意几个关键细节,导致没生效。下面给你两种可靠的实现方式:
方式一:find+move+erase(兼容C++11及以上)
这种方式会在取出线程的同时,把它从map中移除,避免后续误操作空线程:
#include <unordered_map> #include <thread> #include <stdexcept> std::unordered_map<int, std::thread> map_; std::thread get_thread(int handle) { auto it = map_.find(handle); if (it == map_.end()) { throw std::out_of_range("指定的handle不存在"); } // 转移线程所有权到临时变量 std::thread target_thread = std::move(it->second); // 从map中移除已转移的空线程对象 map_.erase(it); return target_thread; }
方式二:用extract(C++17及以上推荐)
C++17给容器新增了extract()方法,可以直接从map中“提取”元素节点,避免手动处理迭代器和erase,代码更简洁:
std::thread get_thread(int handle) { auto node = map_.extract(handle); if (!node) { throw std::out_of_range("指定的handle不存在"); } // 从节点中转移线程所有权 return std::move(node.value()); }
你之前的std::move为啥没生效?
大概率是这两个原因:
- 返回类型不对:如果你的函数返回值是
const std::thread&或者std::thread&,那即使你用了std::move,也无法移动const引用的对象(移动需要修改原对象的状态)。必须把返回类型设为值类型std::thread。 - 没移除map中的空线程:用
std::move(map_.at(handle))之后,map里的那个线程对象会变成“空线程”(有效但不可执行),如果之后你再去访问这个handle对应的元素,调用join()或detach()会直接崩溃,所以一定要记得移除它。
额外注意事项
- 移动后的原线程对象(map里的那个)会处于可析构但不可操作的状态,绝对不能再调用它的任何成员函数(比如
join())。 - 如果你的map是
const修饰的,那也无法移动里面的元素——因为const对象不能被修改,而移动本质是转移所有权,会修改原对象。所以map必须是非const的。
内容的提问来源于stack exchange,提问作者Questionable
相关产品推荐
相关产品推荐

