是否调用reserve
取决于:
- 迭代器类型:对于输入迭代器,实现无法猜测大小
- 库的质量:它可能不专门用于“更好”的迭代器
- 性能是否值得降低可维护性
让我们按顺序取 3 点。
1) 迭代器类型
该assign
方法采用两个迭代器,它们至少必须符合InputIterator
模型。问题是这个模型代表了纯粹的来源(比如来自网络的字节):你可以从中消费两次。结果,给定两个InputIterator
,如果不提取数据就无法计算它们之间的距离(除非您根本不想要数据,但这不是 assign 的目的),因此您不能先“保留”。
至少std::distance
需要FowardIterator
.
2) 实施质量
我不认为标准实际上要求“更好的”迭代器(至少是模型ForwardIterator
)执行assign
两次遍历范围。在受内存带宽限制的计算中(想象在磁带上读取该信息,倒带时间非常慢),这实际上会更昂贵。
但是,许多实现(例如 libc++,见下文)将专门assign
化,以便在存在时ForwardIterator
首先调用std::distance
以在必要时保留正确的内存量。
注意:顺便说一句,这同样适用于大量插入。
3) 维护负担
我会注意到,尽管可能有所收获,但您(可能在不知不觉中)在这里复制了信息。
size_t size = std::distance(begin, end);
if (begin != end) ++begin; // new line
v.reserve(size);
v.assign(begin, end);
看看新行的出现如何突然使代码稍微不正确?并不是说它不起作用,而是声称的优化不再那么正确:您现在预留太多了!
就我个人而言,我相信我的标准库实现会做正确的事情。写它们的人比我有更多的经验。
如果它确实是您的应用程序中已确定的瓶颈,您可以随时尝试自己的方式。只需编写一个reserve_and_assign
方法以使其显而易见,并衡量它是否更好。
作为参考,这里是 libc++ 的实现,取自这里:
template <class _Tp, class _Allocator>
template <class _InputIterator>
typename enable_if
<
__is_input_iterator <_InputIterator>::value &&
!__is_forward_iterator<_InputIterator>::value,
void
>::type
vector<_Tp, _Allocator>::assign(_InputIterator __first, _InputIterator __last)
{
clear();
for (; __first != __last; ++__first)
push_back(*__first);
}
template <class _Tp, class _Allocator>
template <class _ForwardIterator>
typename enable_if
<
__is_forward_iterator<_ForwardIterator>::value,
void
>::type
vector<_Tp, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last)
{
typename iterator_traits<_ForwardIterator>::difference_type __new_size = _VSTD::distance(__first, __last);
if (static_cast<size_type>(__new_size) <= capacity())
{
_ForwardIterator __mid = __last;
bool __growing = false;
if (static_cast<size_type>(__new_size) > size())
{
__growing = true;
__mid = __first;
_VSTD::advance(__mid, size());
}
pointer __m = _VSTD::copy(__first, __mid, this->__begin_);
if (__growing)
__construct_at_end(__mid, __last);
else
this->__destruct_at_end(__m);
}
else
{
deallocate();
allocate(__recommend(static_cast<size_type>(__new_size)));
__construct_at_end(__first, __last);
}
}