实际使用的类型取决于实现。经过分配器 https://en.cppreference.com/w/cpp/named_req/Allocator的要求并在帮助下std::allocator_traits https://en.cppreference.com/w/cpp/memory/allocator_traits特征类模板,任何分配器都可以rebind
通过编辑为另一种类型std::allocator_traits<A>::rebind_alloc<T>
机制。
假设你有一个分配器
template<class T>
class MyAlloc { ... };
如果你写:
std::allocate_shared<T>(MyAlloc<T>{});
这并不意味着MyAlloc<T>
将用于执行分配。如果在内部我们需要分配另一种类型的对象S
,我们可以通过以下方式获得合适的分配器
std::allocator_traits<MyAlloc<T>>::rebind_alloc<S>
它被定义为如果MyAlloc
本身不提供rebind<U>::other
成员类型别名,使用默认实现,返回MyAlloc<S>
。分配器对象是根据传递给的对象构造的std::allocate_shared()
(here, MyAlloc<T>{}
)通过适当的MyAlloc<S>
的转换构造函数(参见分配器要求)。
我们来看看一些特别的执行 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h-libstdc++。对于上面的行,实际分配是由
MyAlloc<std::_Sp_counted_ptr_inplace<T, Alloc<T>, (__gnu_cxx::_Lock_policy)2>
这是合理的:std::allocate_shared()
为包含两者的对象分配内存T
和一个控制块。_Sp_counted_ptr_inplace<T, ...>
就是这样一个物体。它拥有T
在其自身内部_M_storage
数据成员:
__gnu_cxx::__aligned_buffer<T> _M_storage;
同样的机制也用在许多其他地方。例如,std::list<T, Alloc>
使用它来获取一个分配器,然后使用该分配器来分配列表节点,除了T
持有指向其邻居的指针。
一个有趣的相关问题是为什么分配器不是模板模板参数,这似乎是一个自然的选择。正在讨论here https://stackoverflow.com/questions/25572251/in-allocator-aware-stl-classes-why-are-the-allocators-not-template-template-arg.