9
4

1 回答 1

13

我不知道有什么可以让生活更轻松,通过提供所有样板代码allocator_traits 确实使编写分配器变得更简单,但这无助于使用分配器。

因此,我可以在添加<ext/alloc_traits.h>到 GCC 4.7 的 C++03 和 C++11 代码中使用单个分配器 API,类模板提供了在 C++11 模式下使用并直接调用相关成员函数__gnu_cxx::__alloc_traits的一致 API allocator_traitsC++03模式下的分配器。

  1. 不,没有包装器或快捷方式,C++11 分配器要求使容器作者的工作变得更加复杂。每个容器的要求略有不同,具体取决于它管理内存的方式。对于类向量类型,在复制赋值运算符中,如果propagate_on_container_copy_assignment(POCCA) 为 false 并且现有容量大于源对象的大小,那么您可以重新使用现有内存(如果 POCCA 为 true 并且新分配器不是等于您不能重新使用旧内存,因为在分配器被替换后将无法取消分配它)但是这种优化对于基于节点的容器(例如列表或映射)没有多大帮助。

  2. 这看起来几乎是正确的,尽管您可能想要替换

    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_strandm_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));

这让生活稍微轻松一些。

于 2012-06-16T00:35:46.970 回答