将std :: uninitialized_fill()与任何分配器一起使用是否有意义?

时间:2012-03-18 01:39:37

标签: c++

当用户作为参数传递的分配器用于获取内存本身时,在库中用std::uninitialized_fill()初始化内存是否有意义?我问这个是因为分配器应该提供自己的construct()方法(allocate()方法除外),其实现可能与标准方法不同,所以可能std::uninitialized_fill()是并不总是适用于所有情况。

确切地说,我的怀疑来自Stroustrup撰写的C ++书(附录E“标准库异常安全”,E.3.1节),其中作者给出了template<class T, class A> vector<T,A>::vector(size_type n, const T& val, const A& a)的可能实现:分配器a用于获取向量的内存,然后std::uninitialized_fill()用于初始化获得的内存。

他还提供了std::uninitialized_fill()的实现,它在内部使用标准的placement new来初始化内存,但是不再有证据表明分配器的construct()方法作为参数传递给了矢量构造函数。

3 个答案:

答案 0 :(得分:2)

我检查了GCC的vector实现,它的内容如下:

  vector(size_type __n, const value_type& __value,
         const allocator_type& __a = allocator_type())
  : _Base(__n, __a)
  { _M_fill_initialize(__n, __value); }

  ...

  _M_fill_initialize(size_type __n, const value_type& __value)
  {
    this->_M_impl._M_finish =
      std::__uninitialized_fill_n_a(this->_M_impl._M_start, __n, __value,
                                    _M_get_Tp_allocator());
  }

所以,看起来分配器的信息并没有消失。但是uninitialized_fill_n_a的代码很奇怪,它有两个重载(见下文),一个用于通用分配器,另一个用于std::allocator

通用调用constructstd::allocator的特殊调用仅调用std::uninitialized_fill()

所以,我的结论如下,

1)所有std::allocator的{​​{1}}与放置新广告的效果相同

2)对于通用分配器不能假设,或者至少GCC construct没有假设。 (根据@Michael Burr你可以在C ++ 03中假设这一点,不幸的是他没有提到C ++ 11)。

因此使用构造(即std::vector)。

我的个人意见(经过调查),是

i)std::allocator_traits<Alloc>::construct(alloc, pointer, value)有缺陷,因为它应该使用分配器获取最后一个可选参数(或另一个重载),

ii)作为一种解决方法,在您的实现细节中,您应该有一个std::uninitialized_fill函数来完成这项工作,包括处理异常(请注意下面的构造如何在失败时展开而展开)。

iii)当你掌握有关分配器的信息时,当前.uninitialized_fill(first, last, value, alloc)是没用的(基本上你必须重新实现它。)

现在上面提到的代码:

std::unitialized_fill

答案 1 :(得分:1)

我宁愿打电话给construct

提供自定义分配器有多种原因,而且不是唯一的分配策略(使用池/堆栈存储)。

我还看过(并涉及)调试分配器,以帮助跟踪自定义容器中不正确的分配器使用情况。也可以使用分配器来跟踪内存使用情况,收集统计信息等......

因此,construct可能(或不)具有超出对象构造的额外效果,因此不一定等同于简单的展示位置new

在我的调试分配器中,在没有在该地址上调用destroy之前调用construct是错误的(同样在没有调用destroy的情况下回收内存),所以至少我建议保持一致,并将constructdestroy和展示位置new配对,并使用明确的析构函数调用。

答案 2 :(得分:0)

任何非默认分配器的construct()函数的实现都需要具有新的放置效果(根据表32 - C ++ 03中的分配器要求)。

因此,使用std::uninitialized_fill()(定义为在范围内的每个元素上执行新的放置)应该没问题 - 即使使用了自定义分配器。