emplace_back包含std::mutex的对象报construct_at调用匹配错误如何解决
问题根本原因
- 你的
SearchThread类没有显式声明public访问修饰符,构造函数默认是私有权限,外部无法调用构造对象,会触发构造相关报错。 std::mutex、std::condition_variable、std::jthread均属于不可复制、不可移动的类型,你的SearchThread类包含这三类成员,编译器会自动禁用类的默认拷贝构造、拷贝赋值、移动构造、移动赋值函数。std::vector在元素数量超过当前预分配容量时,会申请新的内存块,将已有元素移动/拷贝到新内存再释放旧内存。你在循环调用emplace_back时没有提前预留空间,触发扩容逻辑时,容器尝试移动已有SearchThread对象失败,就会报construct_at相关错误。- 额外注意:你当前构造函数中lambda用
[&]隐式捕获成员变量,一旦SearchThread对象地址发生变化(比如vector扩容移动),线程运行时访问的run、mtx等成员会变成悬空引用,触发未定义行为。
可行的解决方案
方案1:预分配vector容量(临时解决)
先把类的构造函数放到public修饰块中,再在循环构造对象前,提前调用reserve预留和线程数相等的空间,避免运行过程中扩容:
std::vector<SearchThread> search_threads; search_threads.reserve(num_threads); // 新增预分配逻辑 for (int t_id = 0; t_id < num_threads; t_id++) { search_threads.emplace_back(t_id); }
该方案无需修改类核心逻辑,适合固定线程数的场景,但如果后续需要动态新增/删除vector中的元素,依然会触发移动逻辑报错。
方案2:vector存储智能指针(推荐)
将vector的元素类型改为std::unique_ptr<SearchThread>,容器扩容时仅移动指针本身,不涉及SearchThread对象的移动操作,实现简单且稳定:
std::vector<std::unique_ptr<SearchThread>> search_threads; for (int t_id = 0; t_id < num_threads; t_id++) { search_threads.emplace_back(std::make_unique<SearchThread>(t_id)); }
该方案对原有类逻辑改动极小,也支持后续动态调整线程数量,比你提到的把互斥锁/条件变量改成堆指针的方案更优雅,也更容易维护。
方案3:手动实现移动语义
如果确实需要在vector中直接存储对象,你可以手动实现SearchThread的移动构造、移动赋值函数,逻辑是停止原有线程,重新构造不可移动的成员变量:
// 示例移动构造函数,需要声明为public SearchThread(SearchThread&& other) noexcept { // 先停止原对象的线程 { std::unique_lock lock(other.mtx); other.run = false; other.cnd_var.notify_one(); } if (other.thread.joinable()) { other.thread.join(); } // 初始化当前对象的成员 id = other.id; run = true; // 不可移动成员直接重新构造 thread = std::jthread([this]{ while(run){ { std::unique_lock lock(mtx); cnd_var.wait(lock, [this] { return !run; }); if(!run) { return; } } Search(); } }); } // 同理实现移动赋值运算符
该方案实现复杂度最高,适合有特殊需求的场景。
内容的提问来源于stack exchange,提问作者Jane Doe
相关产品推荐
相关产品推荐

