パフォーマンスに関する免責条項:プロファイリングを使用します。
パフォーマンスの考慮:
push_back
は、ベクターの容量が要素を挿入するのに十分である場合は、各コールをチェックする必要があります。コンパイラがループ内でのチェックを避けるほどスマートではない、つまりチェックする必要のあるすべてのループ反復に対して、それ以上の最適化を禁止する可能性もあります。
reserve
への呼び出しがない場合、push_back
は、移動中のベクトルの容量を調整する必要があります。ループ内で既に格納されている要素の移動につながる可能性があります。
swap
はmove
若干異なっている:動きはGManNickGコメントで指摘したように、それは全範囲を取得するように最適化
- は、
vector::insert
は、挿入する前に必要なメモリを確保できた可能性が移動オブジェクトにあまり厳密な保証を、有します挿入される。これはおそらく、ランダムアクセスイテレータの特殊化を必要とするでしょう。それらのためのstd::difference
はO(1)にあります(すべての双方向イテレータに適用できますが、これは2回のループ反復よりも遅くなる可能性があります)。ではなく予約)。
私が考えることができる最も効率的な方法は、必要な容量を確保した後、容量チェックなし(push_back
介し又はinsert
を介してのいずれか)の要素を挿入することです。
スマートスタンダードライブラリの実装では、reserve
をinsert
の中に呼び出して、挿入時の容量をチェックすることはできません。私は完全にこれがcomply to the Standardだろうとは思わない。
あなたのライブラリーは、(コメントを参照)Andy Prowlのバージョン、十分にスマートで十分である場合:他に
dst.insert(dst.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()) );
は、手動でinsert
を呼び出す前にreserve
への呼び出しを記述することができますが、できません(私の知る限り)、インサート/要素を追加W/O内部容量チェック:
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());
}
パフォーマンスの問題は、ループ内の容量チェックのために発生した場合、デフォルト・構築するために必要な追加要素を試みることができます最初。
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);
}
なぜ彼らは(つまり、構築されている)あなたは自分の宛先へのsrcオブジェクトを移動するために、非確認
operator[]
または単純なイテレータを使用することができますが存在する場合'dst.insert(end(dst)、begin(src)、end(src));'? –@AndyProwl:これはアイテムのコピーを作成しませんか?私が間違っていなければ、OPは役に立たないコピーを避けたい。 – syam
@サイヤム:ああ、そう、私はそれを見落とした。したがって、おそらく 'v.insert(end(dst)、make_move_iterator(begin(src))、make_move_iterator(end(src)));'? –