为何std::unordered_map支持不可拷贝/移动类型,而boost::unordered_flat_map不行?
问题:boost::unordered_flat_map编译失败但std::unordered_map正常的原因
先看你的代码:
#include "boost/unordered/unordered_flat_map.hpp" #include <unordered_map> class Foo { public: Foo() = default; explicit Foo(int x) : m_x_(x) {}; private: int m_x_; std::mutex mtx; }; int main(int argc, char** argv) { boost::unordered_flat_map<int,Foo> map_test; // 换成std::unordered_map可正常编译 map_test.try_emplace(1,1); return 0; }
核心原因:两种哈希表的内存布局与扩容机制差异
std::unordered_map的工作方式
它是链式哈希表,底层用数组(桶)+ 链表实现。每个桶里存储指向元素节点的指针,元素本身分散在堆上。当哈希表需要扩容时,只需要重新计算每个元素的哈希值,把节点指针从旧桶移到新桶即可,不需要移动元素本身。
你的try_emplace是在容器内原地构造Foo对象,只要Foo能被原地构造(你的代码满足),不管它能不能移动/拷贝,都能正常工作——毕竟扩容时碰不到元素的移动操作。boost::unordered_flat_map的工作方式
它是扁平哈希表,底层用连续数组直接存储所有元素。当哈希表扩容时,需要把原数组里的所有元素移动或拷贝到新的更大数组中,这就要求元素类型必须支持移动构造(或拷贝构造)。
但你的Foo类里包含std::mutex,标准库的mutex既不能拷贝也不能移动,这导致Foo的移动构造函数被编译器自动删除。当boost的扁平哈希表尝试扩容(哪怕是第一次插入触发的扩容),就会因为无法移动Foo对象而编译失败。
验证与解决方案
- 验证:如果把Foo里的
std::mutex mtx;去掉,boost::unordered_flat_map就能正常编译——此时Foo是可移动的,满足扁平哈希表的要求。 - 解决方案:
- 若必须用boost::unordered_flat_map,可以把mutex包装成可移动的类型,比如用
std::unique_ptr<std::mutex>替代直接的std::mutex成员,让Foo拥有合法的移动构造函数。 - 继续使用std::unordered_map,它的链式布局天然不需要移动元素,适配不可移动的类型。
- 若必须用boost::unordered_flat_map,可以把mutex包装成可移动的类型,比如用
内容的提问来源于stack exchange,提问作者qwark
相关产品推荐
相关产品推荐

