使用std :: allocator_traits <A>

asc*_*ler 8 c++ allocator

我想设计一个类模板,它采用分配器类型(在标准部分17.6.3.5中定义)作为模板参数.我看到如何使用默认设置来std::allocator_traits<A>填充任何缺少的成员A.除此之外,标准库或增强中是否有任何东西可以帮助正确使用分配器?

特别是:

  1. 为了尊重typedef std::allocator_traits<A>::propagate_on_container_copy_assignment,我是否必须在具有类型成员的每个类的特殊成员函数中检查这些内容A?或者是否有一些我可以用作成员的包装类型来代替它来处理这些东西?

  2. 如果我想通过在用户可见对象旁边存储额外数据来进行全面分配以减少分配数量,那么重新绑定分配器是否合适呢?

.

template<typename T, typename A>
class MyClass
{
private:
    //...
    struct storage {
        int m_special_data;
        T m_obj;
    };
    typedef typename std::allocator_traits<A>::template rebind_alloc<storage>
        storage_alloc;
    typedef typename std::allocator_traits<A>::template rebind_traits<storage>
        storage_traits;
    storage_alloc m_alloc;

    static T* alloc(T&& obj)
    {
        storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1);
        sp->m_special_data = 69105;
        return ::new(&sp->m_obj) T(std::move(obj));
    }
    //...
};
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 13

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

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

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

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

    return ::new(&sp->m_obj) T(std::move(obj));
    
    Run Code Online (Sandbox Code Playgroud)

    A a(m_alloc);    
    std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj));
    return &sp->m_obj;
    
    Run Code Online (Sandbox Code Playgroud)

如[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;
Run Code Online (Sandbox Code Playgroud)

m_special_data成员是一个简单的类型,因此只要为其分配存储,它的生命周期就会开始.在m_strm_obj成员需要不平凡的初始化所以他们的寿命开始时,它们的构造完整的,这是由放置新的和做construct呼叫,分别.

编辑:我最近得知标准有一个缺陷(我已经报告过)并且调用construct不需要使用反弹分配器,所以这些行:

    A a(m_alloc);    
    std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj));
Run Code Online (Sandbox Code Playgroud)

可以替换为:

    std::allocator_traits<storage_alloc>::construct(m_alloc, &sp->m_obj, std::move(obj));
Run Code Online (Sandbox Code Playgroud)

这让生活变得更轻松.