性能免责声明:使用分析。
性能考虑:
-
push_back
必须检查每次调用向量的容量是否足以插入元素。编译器不太可能足够聪明来避免循环内的检查,也就是说,对于每个循环迭代,它都必须进行检查,这也可能禁止进一步的优化。
- 如果没有接到电话
reserve
前,push_back
必须随时调整向量的容量,可能在循环内多次调整,这将导致移动已存储的元素。
-
swap
与move
:move 对移动对象的保证不太严格,这可以允许优化
- As G曼尼克G评论中指出,
vector::insert
可以在插入之前保留必要的内存,因为它可以插入整个范围。这可能需要专门研究随机访问迭代器,因为std::difference
对于它们来说是 O(1) (它可以应用于所有双向迭代器,但这可能会慢一些 - 两个循环迭代 - 比not保留)。
我能想到的最有效的方法是保留必要的容量,然后插入元素(通过push_back
or via insert
)无需容量检查。
智能标准库实现可以调用reserve
inside insert
并且在插入时不检查容量。我不完全确定这会符合标准, 尽管。
如果你的图书馆足够聪明,安迪·徘徊的版本(见评论)就足够了:
dst.insert( dst.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()) );
否则,您可以将调用写入reserve
在调用之前手动insert
,但是您不能(据我所知)插入/附加没有内部容量检查的元素:
template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
dst.reserve( dst.size() + std::distance(src_begin, src_end) );
// capacity checks might slow the loop inside `insert` down
dst.insert(dst.end(), src_begin, src_end);
}
Example:
int main()
{
std::vector<int> dst = { 0, 1, 2 };
std::vector<int> src = { 3, 42 };
append( std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()),
dst );
}
实施起来可能会更好append
对于不同的迭代器类型:
template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst,
std::forward_iterator_tag)
{
// let the vector handle resizing
dst.insert(dst.end(), src_begin, src_end);
}
template < typename T, typename RAIt >
void append(RAIt src_begin, RAIt src_end, std::vector<T>& dst,
std::random_access_iterator_tag)
{
dst.reserve( dst.size() + (src_end - src_begin) );
dst.insert(dst.end(), src_begin, src_end);
}
template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
append( src_begin, src_end, dst,
typename std::iterator_traits<FwdIt>::iterator_category() );
}
如果由于循环内部的容量检查而出现性能问题,您可以首先尝试默认构造所需的附加元素。当它们存在时(即已构建),您可以使用非检查operator[]
或简单的迭代器将 src 对象移动到目的地:
template < typename T, typename RAIt >
void append(RAIt src_begin, RAIt src_end, std::vector<T>& dst,
std::random_access_iterator_tag)
{
auto src_size = src_end - src_begin;
dst.resize( dst.size() + src_size );
// copy is not required to invoke capacity checks
std::copy( src_begin, src_end, dst.end() - src_size );
// ^this^ should move with the example provided above
}
方便包装:
template < typename T, typename FwdIt >
void append_move(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
append( std::make_move_iterator(src_begin),
std::make_move_iterator(src_end),
dst );
}