パフォーマンスに関する免責事項: プロファイリングを使用してください。
パフォーマンスに関する考慮事項:
push_back
ベクトルの容量が要素を挿入するのに十分かどうかを呼び出しごとに確認する必要があります。コンパイラがループ内のチェックを回避するほどスマートであるとは考えにくいため、つまり、ループの反復ごとにチェックする必要があり、それ以上の最適化が妨げられる可能性があります。
reserve
beforeへの呼び出しがない場合push_back
、外出先でベクトルの容量を調整する必要があります。おそらくループ内で複数回、既に格納されている要素を移動することになります。
swap
とは少し異なりmove
ます: move は、移動されたオブジェクトに対する厳密な保証が少ないため、最適化が可能になる可能性があります
- GManNickGがコメントで指摘したように、挿入
vector::insert
する範囲全体を取得するため、挿入する前に必要なメモリを予約できます。ランダム アクセス イテレータは O(1) にあるため、これにはおそらく特殊化が必要ですstd::difference
(すべての双方向イテレータに適用できますが、予約しない場合よりも遅くなる可能性があります (2 回のループ イテレーション))。
私が考えることができる最も効率的な方法は、必要な容量を確保してから、容量チェックなしで要素を (push_back
または 経由で) 挿入することです。insert
reserve
スマートな標準ライブラリの実装は、内部への呼び出しを行い、insert
挿入中に容量をチェックしない可能性があります。ただし、これがStandard に準拠するかどうかは完全にはわかりません。
ライブラリが十分にスマートな場合、Andy Prowlのバージョン (コメントを参照) で十分です。
dst.insert( dst.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()) );
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);
}
例:
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 );
}