gpt4 book ai didi

c++ - 使用 std::allocator_traits

转载 作者:可可西里 更新时间:2023-11-01 18:25:49 26 4
gpt4 key购买 nike

我想设计一个类模板,它采用分配器类型(如标准第 17.6.3.5 节中所定义)作为模板参数。我怎么看std::allocator_traits<A>有助于填补 A 的任何缺失成员使用默认设置。除此之外,标准库或 boost 中是否有任何有助于正确使用分配器的内容?

特别是:

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

  2. 如果我想通过在用户可见对象旁边存储额外数据来过度分配以减少分配次数,像这样重新绑定(bind)分配器是否合适?

.

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));
}
//...
};

最佳答案

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

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

  1. 不,没有包装器或快捷方式,C++11 分配器要求使容器作者的工作很多变得更加复杂。每个容器的要求略有不同,具体取决于它管理内存的方式。对于类似 vector 的类型,在复制赋值运算符中 if 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 member 是一个简单的类型,所以它的生命周期在为它分配存储空间后就开始了。 m_strm_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));

这让生活稍微轻松一些。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com