std :: get_temporary_buffer和std :: raw_storage_iterator有任何缺点吗?

时间:2016-09-22 12:56:25

标签: c++ memory stl iterator allocator

最近我听说过get_temporary_bufferraw_storage_iterator,这似乎很棒。几乎在每个副本中都可以避免N个元素的初始化,这可以显着提高性能。

但是我已经使用过C ++了,并没有看到其功能的任何用途。有什么缺点吗?人们为什么不经常使用它们?为什么我不想每次都要使用它们(当然是在一个新的容器中)?

1 个答案:

答案 0 :(得分:0)

感谢蒂姆·宋(Tim Song)今天(可能不是第一次)向我解释这一点。记录下来供后代使用。

raw_storage_iterator的问题在于在出现异常的情况下无法安全使用。

考虑实现非分配器感知的Vector<T>

template<class T>
void Vector<T>::reallocate(int newcap) {
    T *newbuf = (T*)malloc(newcap * sizeof(T));
    Auto( free(newbuf); );
    std::copy(
        buf_, buf_ + size_,
        std::raw_storage_iterator<T*, T>(newbuf)
    );
    std::swap(buf_, newbuf);
    std::swap(cap_, newcap);
    std::destroy(newbuf, newbuf + size_);
}

那个Autothe Auto macro,又名ON_SCOPE_EXIT。它确保我们在击中函数的右花括号时正确地free旧分配;并且如果在到达swap之前抛出异常,我们将释放 new 分配。

但是,如果在std::copy期间引发异常,则释放分配不是我们唯一的责任! std::copy可能已经在T中构造了一些newbuf对象。那些T对象本身可以管理资源。如果抛出异常,则必须在释放T之前调用所有这些newbuf对象的析构函数。否则,我们会发生资源泄漏。

那么,我们应该销毁多少个T对象? ...我们不知道。

raw_storage_iterator在出现异常的情况下无法安全使用。


std::uninitialized_copy(也可以追溯到C ++ 03)没有此问题。

template<class T>
void Vector<T>::reallocate(int newcap) {
    T *newbuf = (T*)malloc(newcap * sizeof(T));
    Auto( free(newbuf); );
    std::uninitialized_copy(buf_, buf_ + size_, newbuf);
    std::swap(buf_, newbuf);
    std::swap(cap_, newcap);
    std::destroy(newbuf, newbuf + size_);
}

std::uninitialized_copy计算成功构造了多少个对象。如果在构造第it个对象时引发了异常,则uninitialized_copy将销毁[[firstit)范围内的每个对象,然后再抛出异常。因此,在这种情况下,我们不会发生资源泄漏。

这是删除raw_storage_iterator而未删除uninitialized_copy的根本原因。前者是异常不安全的;后者是异常安全的。


奖金:raw_storage_iteratoruninitialized_copy都不是分配器感知的,这意味着它们实际上不能构成std::vector之类的分配器感知容器的实现的一部分。每个供应商都必须滚动自己的__uninitialized_copy_a才能在内部使用。但据我所知,分配器感知并不是弃用raw_storage_iterator的因素。杀手是异常安全。