我想设计一个类模板,它将分配器类型(在标准部分17.6.3.5中定义)作为模板参数。我看到std::allocator_traits<A>
如何使用默认设置帮助填充A
的任何缺失成员。除此之外,标准库或提升中是否有任何可以帮助正确使用分配器的内容?
特别是:
要尊重像std::allocator_traits<A>::propagate_on_container_copy_assignment
这样的typedef,我是否必须在每个具有类型A
成员的类的特殊成员函数中检查这些内容?或者是否有一些我可以作为会员使用的包装类型而不会照顾这些东西?
如果我想通过在用户可见对象旁边存储额外数据来进行全面分配以减少分配数量,那么重新分配这样的分配器是否合适呢?
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));
}
//...
};
答案 0 :(得分:12)
我不知道有什么方法可以让生活更轻松,allocator_traits
真的通过提供所有的样板代码来简化编写分配器,但它没有帮助< em>使用分配器。
因此我可以在C ++ 03和C ++ 11代码中使用单个分配器API,我将<ext/alloc_traits.h>
添加到GCC 4.7,类模板__gnu_cxx::__alloc_traits
提供了一个使用{的一致API在C ++ 11模式下{1}}并在C ++ 03模式下直接在分配器上调用相关的成员函数。
不,没有包装器或快捷方式,C ++ 11分配器要求使容器作者的工作更多更复杂。每个容器的要求略有不同,具体取决于它如何管理内存。对于类似矢量的类型,如果allocator_traits
(POCCA)为假并且现有容量大于源对象的大小,则在复制赋值运算符中,则可以重新使用现有内存(如果POCCA为真且新的分配器不相等,你不能重新使用旧的内存,因为在更换分配器之后将无法对其进行解除分配)但是这种优化对基于节点的容器没有多大帮助。作为清单或地图。
虽然您可能想要替换
,但看起来几乎是正确的propagate_on_container_copy_assignment
与
return ::new(&sp->m_obj) T(std::move(obj));
如[container.requirements.general] / 3所述,使用分配器的容器使用A a(m_alloc);
std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj));
return &sp->m_obj;
创建元素类型allocator_traits<A>::construct
本身但分配的任何其他类型(例如您的{{1} }})不得使用T
。
如果构造storage
本身,那么它将构造construct
,除非该成员是一个可以保持未初始化的类型,例如storage
,可以稍后由{{显式初始化1}}。或者,单独构建需要非平凡构造的每个构件,例如,如果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;
成员需要进行非平凡的初始化,因此他们的生命周期在他们的构造函数完成时开始,这分别由placement new和m_special_data
调用完成。
编辑:我最近了解到该标准存在缺陷(我已经报告过),对m_str
的调用不需要使用反弹分配器,所以这些行:
m_obj
可以替换为:
construct
这让生活变得更轻松。