最近,我听说get_temporary_buffer
和raw_storage_iterator
看起来不错。在每个副本中几乎都可以避免初始化N个元素,这可以显着提高性能。
但是我已经使用C ++进行了很多工作,并且在任何地方都没有看到其功能的用法。有什么缺点吗?人们为什么不经常使用它们?为什么我每次要复制时都不使用它们(当然,在新的容器中)?
感谢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,又称Auto
。当我们点击函数的右花括号时,它可以确保我们正确地ON_SCOPE_EXIT
旧分配;并且如果在到达free
之前抛出异常,我们将释放new分配。
但是,如果在swap
期间引发异常,释放分配不是我们的唯一责任! std::copy
可能已经在std::copy
中构造了一些T
对象。这些newbuf
对象本身可以管理资源。如果抛出异常,则在释放T
之前,我们必须
T
对象的析构函数。否则,我们会发生资源泄漏。所以,我们应该销毁多少newbuf
个对象? ...我们不知道。
T
在出现异常的情况下无法安全使用。
raw_storage_iterator
(也可以追溯到C ++ 03)没有此问题。
std::uninitialized_copy
std::uninitialized_copy
计算成功构造了多少个对象。如果在构造第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
弃用的因素。杀手是异常-不安全的。