最近我听说过get_temporary_buffer
和raw_storage_iterator
,这似乎很棒。几乎在每个副本中都可以避免N个元素的初始化,这可以显着提高性能。
但是我已经使用过C ++了,并没有看到其功能的任何用途。有什么缺点吗?人们为什么不经常使用它们?为什么我不想每次都要使用它们(当然是在一个新的容器中)?
答案 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_);
}
那个Auto
是the 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
将销毁[[first
,it
)范围内的每个对象,然后再抛出异常。因此,在这种情况下,我们不会发生资源泄漏。
这是删除raw_storage_iterator
而未删除uninitialized_copy
的根本原因。前者是异常不安全的;后者是异常安全的。
奖金:raw_storage_iterator
和uninitialized_copy
都不是分配器感知的,这意味着它们实际上不能构成std::vector
之类的分配器感知容器的实现的一部分。每个供应商都必须滚动自己的__uninitialized_copy_a
才能在内部使用。但据我所知,分配器感知并不是弃用raw_storage_iterator
的因素。杀手是异常安全。