如何自动初始化含自定义删除器std::shared_ptr的std::array?
问题描述
我正尝试实现一个管理共享资源的类,这些资源的数量在编译时已知,且需在资源释放、析构函数调用前执行自定义操作。我决定采用std::array<std::shared_ptr<...>>作为成员,并搭配自定义删除器:
template <size_t NUM_RESOURCES> class QuirkyMachine { protected: using ResourceDeleter = decltype([](auto resource) { someCustomAction(resource); }); std::array<std::shared_ptr<Resource>, NUM_RESOURCES> _resources; };
请问是否有办法让该数组自动构造使用ResourceDeleter的std::shared_ptr实例,无需显式构造每个指针并通过初始化列表传递?我希望尽可能使用标准库,且需要一个编译时大小固定的集合。
解决方案
当然可以实现,核心思路是利用编译时生成的初始化逻辑或标准库算法,批量构造带自定义删除器的std::shared_ptr,同时保留std::array的编译期固定大小特性,以下是两种可行方案:
方案1:编译期索引遍历初始化(纯编译期逻辑)
借助std::index_sequence生成编译期索引,通过参数包展开批量初始化数组元素,完全在编译阶段完成构造逻辑:
#include <array> #include <memory> #include <utility> // 假设的资源类型与自定义操作 struct Resource {}; void someCustomAction(Resource* resource) {} template <size_t NUM_RESOURCES> class QuirkyMachine { protected: using ResourceDeleter = decltype([](auto* resource) { someCustomAction(resource); }); std::array<std::shared_ptr<Resource>, NUM_RESOURCES> _resources; // 辅助构造函数:利用编译期索引序列批量初始化 template <size_t... Idx> QuirkyMachine(std::index_sequence<Idx...>) : _resources{ (std::shared_ptr<Resource>(nullptr, ResourceDeleter{}))... } {} public: QuirkyMachine() : QuirkyMachine(std::make_index_sequence<NUM_RESOURCES>{}) {} };
这里std::make_index_sequence<NUM_RESOURCES>会生成0到NUM_RESOURCES-1的编译期索引列表,再通过参数包展开(std::shared_ptr<Resource>(nullptr, ResourceDeleter{}))...,自动为数组的每个元素构造带自定义删除器的空智能指针。
方案2:标准库算法批量填充(运行时简洁实现)
先给带自定义删除器的智能指针定义别名,再用std::fill或std::generate完成批量初始化,代码更简洁直观:
#include <array> #include <memory> #include <algorithm> struct Resource {}; void someCustomAction(Resource* resource) {} template <size_t NUM_RESOURCES> class QuirkyMachine { protected: using ResourceDeleter = decltype([](auto* resource) { someCustomAction(resource); }); // 定义带指定删除器的智能指针别名 using SharedResource = std::shared_ptr<Resource>; std::array<SharedResource, NUM_RESOURCES> _resources; public: QuirkyMachine() { // 批量填充空的带删除器智能指针 std::fill(_resources.begin(), _resources.end(), SharedResource(nullptr, ResourceDeleter{})); } };
如果需要在初始化时就分配资源(而非空指针),可以替换为std::generate调用资源创建函数:
// 假设的资源创建函数 Resource* createResource() { return new Resource{}; } // 构造函数内修改为: std::generate(_resources.begin(), _resources.end(), [](){ return SharedResource(createResource(), ResourceDeleter{}); });
注意事项
- 当自定义删除器是无捕获lambda时,
std::shared_ptr的内存占用和普通版本一致,不会有额外开销;若lambda带有捕获变量,删除器会占用额外内存空间,需要注意成本。 - 若要求完全编译期完成初始化,优先选择方案1;若追求代码简洁,方案2的标准库算法实现更易维护。
内容的提问来源于stack exchange,提问作者pSoLT
相关产品推荐
相关产品推荐

