std::pmr::unordered_map结合自定义分配器构建树结构的问题咨询
问题:使用PMR分配器构建树状结构时的不完全类型与内存分配问题
我想为std::pmr::unordered_map使用自定义分配器,让元素和链式结构都通过该分配器分配,但构建树状数据结构时遇到了问题。
给出的代码如下:
#include <unordered_map> class A { public: using allocator_type = Mallocator; // 请勿删除,用于为类启用作用域分配 using NodeMap = std::pmr::unordered_map<std::size_t, A>; NodeMap nodes_ = {}; };
这段代码在Windows MSVC编译器下可以编译通过,但在Linux GCC编译器中失败——GCC正确拒绝了代码,因为类A是不完全类型却被用于NodeMap。
我的第一想法是在map中改用std::unique_ptr,即using NodeMap = std::pmr::unordered_map<std::size_t, std::unique_ptr<A>>;,但担心unique_ptr及其指向的A对象都会在自定义分配器之外分配。请问是否存在PMR版本的std::unique_ptr?请提供替代方案。
补充信息:
- 最新版本编译器不会触发该报错
- GCC 11的错误输出:
pt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/stl_pair.h: In instantiation of ‘struct std::pair’: /opt/rh/gcc-toolset-11/root/usr/include/c++/11/ext/aligned_buffer.h:91:28: required from ‘struct __gnu_cxx::__aligned_buffer<pair<const long unsigned int, A> >’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:234:43: required from ‘struct std::__detail::_Hash_node_value_base<pair<const long unsigned int, A> >’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:268:12: required from ‘struct std::__detail::_Hash_node_value<pair<const long unsigned int, A>, false>’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:277:12: required from ‘struct std::__detail::_Hash_node<pair<const long unsigned int, A>, false>’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:1812:13: required from ‘struct std::__detail::_Hashtable_alloc<std::pmr::polymorphic_allocator<std::__detail::_Hash_node<pair<const long unsigned int, A>, false> > >’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:811:13: required from ‘struct std::__detail::_Insert_base<long unsigned int, pair<const long unsigned int, A>, std::pmr::polymorphic_allocator<pair<const long unsigned int, A> >, std::__detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable_policy.h:1004:12: required from ‘struct std::__detail::_Insert<long unsigned int, pair<const long unsigned int, A>, std::pmr::polymorphic_allocator<pair<const long unsigned int, A> >, std::__detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true>, false>’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/hashtable.h:180:11: required from ‘class std::_Hashtable<long unsigned int, pair<const long unsigned int, A>, std::pmr::polymorphic_allocator<pair<const long unsigned int, A> >, std::__detail::_Select1st, std::equal_to<long unsigned int>, std::hash<long unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >’ /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/unordered_map.h:105:18: required from ‘class std::unordered_map<long unsigned int, A, std::hash<long unsigned int>, std::equal_to<long unsigned int>, std::pmr::polymorphic_allocator<pair<const long unsigned int, A> > >’ fileA.cpp:121:20: required from here /opt/rh/gcc-toolset-11/root/usr/include/c++/11/bits/stl_pair.h:218:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
解决方案
1. 解决不完全类型的直接办法
C++标准中,容器实例化时原本要求元素类型是完全类型,但GCC 12+和MSVC对std::pmr::unordered_map放宽了限制——因为PMR分配器的设计允许延迟分配,不需要在容器实例化时确定元素大小。如果可以升级到GCC 12及以上版本,原代码可以直接编译通过。
2. 结合智能指针与PMR分配器的替代方案
不存在专门的PMR版本std::unique_ptr,但可以通过自定义删除器+PMR分配器,确保A对象的内存完全由指定分配器管理:
#include <unordered_map> #include <memory> #include <memory_resource> class A; // 自定义删除器,绑定PMR分配器用于内存释放 struct PMRDeleter { std::pmr::polymorphic_allocator<A> alloc; void operator()(A* ptr) const { if (ptr) { alloc.destroy(ptr); alloc.deallocate(ptr, 1); } } }; using PMRUniquePtr = std::unique_ptr<A, PMRDeleter>; class A { public: using allocator_type = std::pmr::polymorphic_allocator<A>; using NodeMap = std::pmr::unordered_map<std::size_t, PMRUniquePtr>; NodeMap nodes_; // 用指定分配器创建A实例的静态方法 static PMRUniquePtr create(allocator_type alloc) { A* ptr = alloc.allocate(1); try { alloc.construct(ptr); return PMRUniquePtr(ptr, PMRDeleter{alloc}); } catch (...) { alloc.deallocate(ptr, 1); throw; } } };
这个方案中:
NodeMap的所有内部结构(哈希表节点、链式结构)由PMR分配器管理A对象的内存分配、构造、销毁、释放全由指定的PMR分配器完成- 自定义删除器会绑定对应的分配器,确保释放逻辑和分配逻辑一致
3. 其他可选方案
如果无法升级编译器且不想用智能指针,可以将NodeMap的定义延迟到A完全类型之后,比如用指针间接持有容器,但这种方式会增加代码复杂度,不推荐作为首选。
内容的提问来源于stack exchange,提问作者User 10482
相关产品推荐
相关产品推荐

