Adr*_*ian 10 c++ algorithm c++11
我当时认为vector::insert()和std::copy()命令需要额外的分配.但是,如果我push_back()是一个新创建的元素,那么swap()我认为只要包含的类型没有使用默认构造函数分配,这将减少任何分配.
我的问题实际上是针对std::vectors类型的std::string,但应该适用于此处所述的其他包含类型:
template <typename T>
void appendMove(std::vector<T>& dst, std::vector<T>& src)
{
dst.reserve(dst.size() + src.size())
for(std::vector<T>::iterator it = src.begin(); it != src.end(); ++it)
{
dst.push_back(std::vector<T>());
std::swap(dst.end()[-1], *it);
}
}
Run Code Online (Sandbox Code Playgroud)
我对么?我错过了什么吗?也许还有更好的方法吗?
dyp*_*dyp 12
性能免责声明:使用性能分析.
性能考虑:
push_back如果向量的容量足以插入元素,则必须检查每个调用.编译器不太可能足够智能以避免在循环内进行该检查,也就是说,对于它必须检查的每个循环迭代,这也可能禁止进一步的优化.reserve之前没有调用,则push_back必须在移动中调整向量的容量,可能在循环内多次调整,这将导致移动已存储的元素.swap与movemove 略有不同:move对移动的对象有较少的严格保证,这可能允许优化vector::insert可以在插入之前保留必要的内存,因为它可以插入整个范围.这可能需要对随机访问迭代器进行专门化,因为std::difference它们在O(1)中(它可以应用于所有双向迭代器,但这可能更慢 - 两次循环迭代 - 而不是保留).我能想到的最有效的方法是保留必要的容量,然后在没有容量检查的情况下插入元素(通过push_back或通过insert).
智能标准库实现可以对reserve内部进行调用,insert而不是在插入期间检查容量.不过,我不完全确定这会符合标准.
如果您的图书馆足够聪明,Andy Prowl的版本(见评论)就足够了:
dst.insert( dst.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()) );
Run Code Online (Sandbox Code Playgroud)
否则,您可以在调用reserve之前手动编写调用insert,但是您不能(AFAIK)插入/追加没有内部容量检查的元素:
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);
}
Run Code Online (Sandbox Code Playgroud)
例:
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 );
}
Run Code Online (Sandbox Code Playgroud)
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() );
}
Run Code Online (Sandbox Code Playgroud)
如果由于循环内的容量检查而出现性能问题,您可以尝试首先默认构造所需的其他元素.当它们存在时(即已经构造),您可以使用非检查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
}
Run Code Online (Sandbox Code Playgroud)
便利包装:
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 );
}
Run Code Online (Sandbox Code Playgroud)