C++ 对任何分配器使用std::uninitialized_fill()有意义吗?

C++ 对任何分配器使用std::uninitialized_fill()有意义吗?,c++,C++,当用户使用作为参数传递的分配器获取内存时,在库中使用std::uninitialized_fill()初始化内存有意义吗?我这样问是因为分配器应该提供自己的construct()方法(而不是allocate()方法),其实现可能不同于标准方法,因此可能std::uninitialized_fill()并不总是适用于所有情况 确切地说,我的疑虑来自Stroustrup编写的C++书籍(附录E标准库异常安全性,E部分3.1),作者给出了一个可能的实现:模板向量::向量(siZeHythyType N

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

确切地说,我的疑虑来自Stroustrup编写的C++书籍(附录E标准库异常安全性,E部分3.1),作者给出了一个可能的实现:<代码>模板向量::向量(siZeHythyType N,const t& var,const a&a)< /> >:使用分配器A来获取向量的内存,然后使用

std::uninitialized_fill()
初始化获得的内存


他还给出了
std::uninitialized_fill()
的实现,该实现在内部使用标准布局new来初始化内存,但不再有
构造()的证据
作为参数传递给向量构造函数的分配器的方法。

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


因此,使用
std::uninitialized_fill()
(定义为对范围中的每个元素执行新的放置)应该可以-即使使用了自定义分配器。

我宁愿调用
构造

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

我还看到(并涉足)调试分配器,以帮助跟踪自定义容器中不正确的分配器使用情况。还可以使用分配器跟踪内存使用情况、收集统计数据等

因此,
构造
可能(或不)具有对象构造之外的其他效果,因此不一定等同于简单的放置
新建


在我的调试分配器中,调用
destroy
而之前没有对该地址调用
construct
是一个错误(同样,在没有调用
destroy
的情况下回收内存),因此,至少我建议保持一致,将
construct
destroy
配对,并使用显式析构函数调用放置
new

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

  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());
  }
因此,分配器的信息似乎不会消失。然而,
未初始化的\u fill\u n\u a
的代码很奇怪,它有两个重载(见下文),一个用于通用分配器,另一个用于
std::分配器

通用调用,
construct
和专用for
std::allocator
只是调用
std::uninitialized\u fill()

那么我的结论是,

1) 所有
std::allocator
construct
s与新的和新的布局具有相同的效果

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

因此使用构造(即
std::allocator\u traits::construct(alloc、指针、值)
)。

我个人的意见(在调查之后)是

i)
std::uninitialized_fill
有缺陷,它应该使用分配器接受最后一个可选参数(或另一个重载)

ii)作为一种解决方法,在您的实现细节中,您应该有一个
.uninitialized_fill(first、last、value、alloc)
函数来完成该工作,包括异常处理(注意下面的构造如何在失败时销毁)

iii)当前的
std::unitialized_fill
在您拥有有关分配器的信息时是非常无用的(基本上您必须重新实现它)


现在,上面引用的代码:

  template<typename _ForwardIterator, typename _Size, typename _Tp,
           typename _Allocator>
    _ForwardIterator
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
                             const _Tp& __x, _Allocator& __alloc)
    {
      _ForwardIterator __cur = __first;
      __try
        {
          typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
          for (; __n > 0; --__n, ++__cur)
            __traits::construct(__alloc, std::__addressof(*__cur), __x);
          return __cur;
        }
      __catch(...)
        {
          std::_Destroy(__first, __cur, __alloc);
          __throw_exception_again;
        }
    }

  template<typename _ForwardIterator, typename _Size, typename _Tp,
           typename _Tp2>
    inline _ForwardIterator
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
                             const _Tp& __x, allocator<_Tp2>&)
    { return std::uninitialized_fill_n(__first, __n, __x); }
模板
_前向迭代器
__未初始化的填充(\u ForwardIterator\u first,\u Size\u n,
常量(Tp和x,分配器和alloc)
{
_ForwardIterator uu cur=uuu first;
__试一试
{
typedef uu gnu cxx::u alloc_traits uu traits;
对于(;\uuu n>0;--\uu n,++\uu cur)
__特征:构造(uuu alloc,std::uuu addressof(*uuuu cur),uuux);
返回当前值;
}
__捕获(…)
{
std::_Destroy(uuu first,uuu cur,uuu alloc);
__再次抛出异常;
}
}
模板
内联转发器迭代器
__未初始化的填充(\u ForwardIterator\u first,\u Size\u n,
常量Tp和x,分配器&)
{return std::未初始化的填充(uu first,uu n,uu x)}

什么是
std::initialize_fill()
?你是说
std::uninitialized_fill()
?<代码>初始化AlgIsFILE()/<代码>函数在C++标准中的任何地方都不出现,与 unInLaZiZeDeFILL()/代码>不同。非常感谢。我修正了这个问题。这似乎与我的问题有关:,我确实认为实际上使用普通的
未初始化的填充
可能是安全的,你基本上是在
std::未初始化的填充(数据、大小、值)之间做决定吗
std::for_each(数据,数据+大小,[&value,this](自动生成){std::分配器特性::构造(this->allocator,it,value);})?看起来到目前为止的答案还没有定论,对吧?我想他是在问,用未初始化的填充替换调用
构造
是否可以,这是不同的<代码>施工