自定义Allocator特化用于std::allocate_shared时未被调用的问题
问题根源
你猜的没错——std::allocate_shared实际分配的不是目标对象本身的内存,而是一块包含目标对象+shared_ptr控制块(引用计数、销毁器等)的复合内存块。这块内存的类型是编译器内部生成的匿名模板类,而非你定义的General或Special。
你为Allocator<Special>写的特化之所以没被调用,是因为std::allocate_shared会通过Allocator<Special>的rebind模板,将分配器类型转换为Allocator<内部匿名类型>,而这个类型匹配的是通用版Allocator模板,而非你的特化版本。
解决方法
要让Special类的shared_ptr使用专用内存池,需要让分配器的内存池选择逻辑不依赖于分配的具体类型,而是绑定到目标对象的“专属标识”上。这里提供两种可行方案:
方案1:基于标签的分配器设计
重新设计分配器,用标签类型区分内存池,让rebind后仍保留原标签的内存池逻辑:
// 通用内存池实现 struct GeneralPool { static void* allocate(size_t size) { printf("通用内存池分配\n"); return malloc(size); } static void deallocate(void* p) { free(p); } }; // Special专用内存池实现 struct SpecialPool { static void* allocate(size_t size) { printf("Special专用内存池分配\n"); return malloc(size); } static void deallocate(void* p) { free(p); } }; // 基于标签的分配器模板 template<typename PoolTag> struct TaggedAllocator { using value_type = PoolTag; // 关键:rebind时保留原标签,确保不管分配什么类型都用对应内存池 template<typename U> struct rebind { using other = TaggedAllocator<PoolTag>; }; TaggedAllocator() = default; template<typename OtherTag> TaggedAllocator(const TaggedAllocator<OtherTag>&) {} template<typename U> U* allocate(size_t n) { return static_cast<U*>(PoolTag::allocate(n * sizeof(U))); } template<typename U> void deallocate(U* p, size_t) { PoolTag::deallocate(p); } }; // 使用方式 auto general_sp = std::allocate_shared<General>(TaggedAllocator<GeneralPool>{}); auto special_sp = std::allocate_shared<Special>(TaggedAllocator<SpecialPool>{});
这种设计下,不管std::allocate_shared把分配器rebind到什么内部类型,最终都会使用你指定的内存池。
方案2:修改分配器的rebind逻辑(针对原有特化)
如果你不想大幅修改原有分配器结构,可以调整通用Allocator的rebind模板,让从Allocator<Special>rebind而来的分配器仍使用特化逻辑:
#include <type_traits> template<typename T> struct Allocator { using value_type = T; // 针对Special的rebind特殊处理:返回Allocator<Special>而非Allocator<U> template<typename U> struct rebind { using other = std::conditional_t<std::is_same_v<T, Special>, Allocator<Special>, Allocator<U>>; }; Allocator() = default; template<typename U> Allocator(const Allocator<U>&) {} T* allocate(size_t n) { printf("通用分配器,类型:%s\n", typeid(T).name()); return static_cast<T*>(malloc(n * sizeof(T))); } void deallocate(T* p, size_t) { free(p); } }; // 保留原特化 template<> struct Allocator<Special> { using value_type = Special; template<typename U> struct rebind { using other = Allocator<Special>; }; Special* allocate(size_t n) { printf("Special专用分配器\n"); return static_cast<Special*>(malloc(n * sizeof(Special))); } void deallocate(Special* p, size_t) { free(p); } };
这样,当std::allocate_shared用Allocator<Special>rebind到内部类型时,得到的还是Allocator<Special>,会调用你的特化版本。
内容的提问来源于stack exchange,提问作者user28895669
相关产品推荐
相关产品推荐

