1 回答
我不知道有什么可以让生活更轻松,通过提供所有样板代码allocator_traits
确实使编写分配器变得更简单,但这无助于使用分配器。
因此,我可以在添加<ext/alloc_traits.h>
到 GCC 4.7 的 C++03 和 C++11 代码中使用单个分配器 API,类模板提供了在 C++11 模式下使用并直接调用相关成员函数__gnu_cxx::__alloc_traits
的一致 API allocator_traits
C++03模式下的分配器。
不,没有包装器或快捷方式,C++11 分配器要求使容器作者的工作变得更加复杂。每个容器的要求略有不同,具体取决于它管理内存的方式。对于类向量类型,在复制赋值运算符中,如果
propagate_on_container_copy_assignment
(POCCA) 为 false 并且现有容量大于源对象的大小,那么您可以重新使用现有内存(如果 POCCA 为 true 并且新分配器不是等于您不能重新使用旧内存,因为在分配器被替换后将无法取消分配它)但是这种优化对于基于节点的容器(例如列表或映射)没有多大帮助。这看起来几乎是正确的,尽管您可能想要替换
return ::new(&sp->m_obj) T(std::move(obj));
和
A a(m_alloc); std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); return &sp->m_obj;
如 [container.requirements.general]/3 中所述,使用分配器的容器用于allocator_traits<A>::construct
创建元素类型T
本身,但分配的任何其他类型(例如您的storage
)不得使用construct
.
如果storage
自身被构造,那么它将构造storage::m_obj
,除非该成员是可以保持未初始化的类型,例如std::aligned_storage<sizeof(T)>
,可以稍后显式初始化allocator_traits<A>::construct
。或者,单独构造每个需要非平凡构造的成员,例如如果storage
还有一个string
成员:
storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1);
sp->m_special_data = 69105;
::new (&sp->m_str) std::string("foobar");
A a(m_alloc);
std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj));
return &sp->m_obj;
该m_special_data
成员是一个普通类型,因此一旦为其分配存储空间,它的生命周期就开始了。m_str
andm_obj
成员需要非平凡的初始化,因此它们的生命周期在构造函数完成时开始,这分别由放置 new 和调用construct
完成。
编辑:我最近了解到该标准有一个缺陷(我已经报告过)并且调用construct
不需要使用反弹分配器,所以这些行:
A a(m_alloc);
std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj));
可以替换为:
std::allocator_traits<storage_alloc>::construct(m_alloc, &sp->m_obj, std::move(obj));
这让生活稍微轻松一些。