不同类型的 Allocator 能否释放彼此分配的存储?
结论
可以,前提是两个 allocator 满足适用的 allocator 要求,而且它们的相等关系能保证跨实例释放安全。类型不同本身并不妨碍释放,关键是两种类型之间是否存在标准定义的兼容关系。
不过,相等只说明一方分配的存储能否由另一方释放。它不会自动让任意两种 allocator 类型互相转换,也不代表它们的指针类型、对象构造和销毁机制都兼容。
C++ allocator 要求如何定义相等
C++ allocator 要求分别规定了同类型 allocator 和对应的不同类型 allocator 如何比较。同类型 allocator 满足 a1 == a2 时,由其中一个分配的存储可以由另一个释放。对于类型 X 和其对应类型 Y 的 allocator,要求还规定了 a == b 的比较方式:先将 b 重绑定为 X 对应的 allocator 类型,再进行比较。能否进行这种跨类型比较,取决于 allocator 是否满足相应要求,以及重绑定是否成立。C++ draft:Allocator requirements
因此,不能仅凭两个 allocator“看起来使用同一内存池”就认定它们可以互换。应根据 allocator 的标准接口和比较语义来判断。
对 std::polymorphic 的影响
题目中的 std::allocator<T> 和 std::allocator<U> 是相应的 allocator 特化;不同 T 的 std::pmr::polymorphic_allocator<T> 也提供了跨类型比较机制。对于这些情况,allocator 要求可以作为跨类型相等的依据。
当前 C++ draft 中,std::polymorphic 的移动构造和带 allocator 的移动构造都接收同一个 polymorphic<T, Allocator> 特化。带 allocator 的移动构造会比较目标对象和源对象的 allocator:相等时可以接管对象;不相等时,则使用目标 allocator 移动构造对象。这里规定的是同一 Allocator 类型下的重载,没有题目所示的任意 A 到 Allocator 的跨类型转换重载。C++ draft:std::polymorphic constructors
排除跨 allocator 类型的重载,并不是因为不同类型绝对不能释放。这样的重载还需要约束 allocator 转换或跨类型相等关系,并确保指针类型和对象销毁路径兼容。不同 allocator 类型的构造、销毁操作也可能不同;存储释放兼容本身不足以保证整个所有权转移正确。
设计时的处理方式
跨类型移动所有权时,可以按以下思路区分两种情况:
若目标 allocator 与源 allocator 的兼容关系有明确保证:
接管对象,并确保后续销毁与释放使用兼容的 allocator
否则:
使用目标 allocator 移动构造对象
再用源 allocator 销毁并释放原对象
这只是设计思路示意,并非 std::polymorphic 的实际 API。若要实现跨类型重载,还需规定 A 与 Allocator 如何比较或转换,并确认 allocator_traits、指针类型和对象销毁方式都适用。
注意事项
- 不要把“能比较”理解为“任意类型的 allocator 都能比较”。标准描述的是满足 allocator 要求的对应类型关系。
std::allocator或std::pmr::polymorphic_allocator的具体性质不能推广到所有用户自定义 allocator。- 若两个 allocator 不满足释放兼容条件,就不能让目标 allocator 直接释放源对象的存储;应使用目标 allocator 重新构造对象。
备注:内容仅供参考。
