如何使用pmr分配器深拷贝指向pmr资源的unique_ptr?
PMR环境下unique_ptr深拷贝的分配器适配方案
问题背景
用多态内存资源(PMR)重写库的时候碰到个标准没覆盖的坑:我的类里存了个unique_ptr,指向用PMR分配的资源(比如pmr::vector),拷贝构造时得做深拷贝。本来想直接用分配器的new_object()创建对象,再交给unique_ptr管理,但遇到两个问题:
- 分配器不是合法的删除函子,没法直接当
unique_ptr的删除器用——它没重载operator()来执行删除操作 delete_object()是分配器的非静态成员函数,不能直接传给unique_ptr当删除逻辑
举个出错的代码例子:
#include <memory_resource> #include <memory> #include <vector> class MyClass { public: using Allocator = std::pmr::polymorphic_allocator<std::byte>; MyClass(Allocator alloc = {}) : alloc_(alloc) { // 用new_object创建pmr::vector,但unique_ptr的默认删除器会用普通delete,不对 vec_ = std::unique_ptr<std::pmr::vector<int>>(alloc_.new_object<std::pmr::vector<int>>(alloc_)); } MyClass(const MyClass& other) : alloc_(other.alloc_) { // 尝试深拷贝,但同样面临删除器不匹配的问题 vec_ = std::unique_ptr<std::pmr::vector<int>>(alloc_.new_object<std::pmr::vector<int>>(*other.vec_, alloc_)); } ~MyClass() { // 只能手动调用delete_object,没法让unique_ptr自动处理 if (vec_) { alloc_.delete_object(vec_.release()); } } private: Allocator alloc_; std::unique_ptr<std::pmr::vector<int>> vec_; };
编译器会报类似错误:
error: static assertion failed: unique_ptr's deleter must be invocable with a pointer to the type
note: 'std::default_delete<std::pmr::vector>' is not callable with an argument of type 'std::pmr::vector *'
解决方案:自定义PMR适配删除器
核心思路是写个自定义删除器,把PMR分配器和delete_object()的调用逻辑绑定起来,让unique_ptr能正确调用PMR的释放逻辑。
1. 实现自定义删除器
这个删除器要持有PMR分配器,并重载operator()来调用分配器的delete_object():
template <typename T> struct PMRDeleter { using Allocator = std::pmr::polymorphic_allocator<std::byte>; PMRDeleter() = default; explicit PMRDeleter(Allocator alloc) : alloc_(std::move(alloc)) {} void operator()(T* ptr) const { if (ptr) { alloc_.delete_object(ptr); } } private: Allocator alloc_; };
2. 修改unique_ptr的类型
把原来的unique_ptr改成带自定义删除器的版本:
using VecPtr = std::unique_ptr<std::pmr::vector<int>, PMRDeleter<std::pmr::vector<int>>>; VecPtr vec_;
3. 调整构造函数逻辑
创建unique_ptr时,要把分配器同时传给new_object()和删除器,确保创建和释放用的是同一个PMR资源:
// 默认构造函数 MyClass(Allocator alloc = {}) : alloc_(alloc), vec_(alloc.new_object<std::pmr::vector<int>>(alloc), PMRDeleter<std::pmr::vector<int>>(alloc)) {} // 拷贝构造函数(深拷贝) MyClass(const MyClass& other) : alloc_(other.alloc_) { // 用当前对象的分配器创建新的vector,并拷贝原数据 vec_ = VecPtr(alloc_.new_object<std::pmr::vector<int>>(*other.vec_, alloc_), PMRDeleter<std::pmr::vector<int>>(alloc_)); }
4. 移除手动析构逻辑
现在unique_ptr会自动调用自定义删除器来释放资源,所以可以删掉原来手动调用delete_object()的析构函数。
完整可运行代码
#include <memory_resource> #include <memory> #include <vector> template <typename T> struct PMRDeleter { using Allocator = std::pmr::polymorphic_allocator<std::byte>; PMRDeleter() = default; explicit PMRDeleter(Allocator alloc) : alloc_(std::move(alloc)) {} void operator()(T* ptr) const { if (ptr) { alloc_.delete_object(ptr); } } private: Allocator alloc_; }; class MyClass { public: using Allocator = std::pmr::polymorphic_allocator<std::byte>; using VecPtr = std::unique_ptr<std::pmr::vector<int>, PMRDeleter<std::pmr::vector<int>>>; MyClass(Allocator alloc = {}) : alloc_(alloc), vec_(create_vec(alloc)) {} // 深拷贝构造 MyClass(const MyClass& other) : alloc_(other.alloc_) { vec_ = create_vec(alloc_, *other.vec_); } // 移动构造和赋值默认即可 MyClass(MyClass&&) noexcept = default; MyClass& operator=(MyClass&&) noexcept = default; // 拷贝赋值运算符 MyClass& operator=(const MyClass& other) { if (this != &other) { alloc_ = other.alloc_; vec_ = create_vec(alloc_, *other.vec_); } return *this; } private: // 封装vector创建逻辑,减少重复代码 static VecPtr create_vec(Allocator alloc, const std::pmr::vector<int>& init = {}) { if (init.empty()) { return VecPtr(alloc.new_object<std::pmr::vector<int>>(alloc), PMRDeleter<std::pmr::vector<int>>(alloc)); } else { return VecPtr(alloc.new_object<std::pmr::vector<int>>(init, alloc), PMRDeleter<std::pmr::vector<int>>(alloc)); } } Allocator alloc_; VecPtr vec_; };
额外说明
polymorphic_allocator是轻量级可拷贝的,拷贝后依然指向同一个内存资源,所以存在删除器里完全安全。- 如果想更轻量,也可以在删除器里存
std::pmr::memory_resource*代替整个分配器,但要保证内存资源的生命周期比unique_ptr长。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

