You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:02:12