同様の質問が here と here に存在しますが、明確な答えが得られなかったと思うので、質問を作り直します。
C++ 11標準では、std :: vector :: Push_backとstd :: vector :: emplace_backの2つの方法でベクターの最後に新しい要素を追加しています。
それらの違いは、std :: vector :: emplace_backがオブジェクトを適切な場所に構築し、std :: vector :: Push_backが基本的にオブジェクト(またはプリミティブ型)をコピーするか、ベクターの最後に移動することです。
次に、std :: vector :: Push_backは、std :: vectorにプリミティブ型を追加するためのより良いオプションを探します。例えば:
std::vector<int> vVec;
vVec.Push_back(10);
int iVar 30;
vVec.Push_back(iVar);
しかし、オブジェクトについて話すとき、私にはそれほど明確ではありません。たとえば、std :: stringのベクターを見てみましょう。リテラル文字列を追加する場合はstd :: vector :: emplace_backを使用し、文字列オブジェクトをコピーまたは移動する場合はstd :: vector :: Push_back?
私が何を求めているかをより明確にするために、いくつかのシナリオを見てみましょう:
最初のシナリオ:
std::vector<string> vVec;
std::string sTest(“1st scenario”);
vVec.Push_back(sTest);
vVec.emplace_back(sTest);
Push_backはsTestのコピーを作成し、vVecの末尾にある新しい要素に関連付けます。一方、emplace_backはvVecの末尾に文字列オブジェクトを作成し、sTestのコンテンツをその中にコピーします。この場合、どちらがより効率的か、重要ではありませんか?
2番目のシナリオ:
std::vector<string> vVec;
std::string sTest1(“2st scenario”);
std::string sTest2(“2st scenario”);
vVec.Push_back(move(sTest1));
vVec.emplace_back(move(sTest2));
Push_backはセマンティックを移動する準備ができていますが、emplace_backはどうなりますか?この場合、どちらがより効率的ですか?
3番目のシナリオ:
std::vector<string> vVec;
std::string sTest("3st scenario");
vVec.Push_back(sTest.substr(4,4));
vVec.emplace_back(sTest.substr(4,4));
Std :: string :: substrが別のstd :: stringを返すため、2番目のシナリオと同じ状況になりますか?
結論
std :: vector :: emplace_backがstd :: vector :: Push_backよりもリテラルが含まれている場合(その場での構築)にのみ効率的である場合、それを使用することは本当に正当ですか?もしそうなら、私にいくつかの例を挙げてもらえますか?
Std :: vector :: emplace_backがc ++ 0xではなくc ++ 11に実装されたことに注意することが重要です。それが存在するのには十分な理由があると思います。
Ascheplerが言ったようにstd :: vector :: emplaceを使用する利点を実際に理解するには少し時間がかかります。
私はそれが構築されたときにいくつかのデータを受け取る独自のクラスがある場合に使用するより良いシナリオを見つけました。
より明確にするために、次のように仮定します。
次に、次のような行を作成します。
vVec.emplace(get1stElem(), get2ndElem(), get3rdElem());
次に、std :: vector :: emplaceは、std :: vector :: Push_backよりも効率的にMyObjectを適切に構築します。
両方の関数がシナリオ1で1つのコピーコンストラクターを呼び出し、シナリオ2または3で1つの移動コンストラクターを呼び出すため、3つのシナリオで大きな違いはありません。
しかし、10 'x'
文字の文字列を作成したい場合はどうでしょうか。この場合、選択肢は
vVec.Push_back(std::string(10, 'x'));
vVec.emplace_back(10, 'x');
この場合、Push_back
はカスタムstring
コンストラクターを呼び出してから移動コンストラクターを呼び出しますが、emplace_back
はカスタムstring
コンストラクターを直接呼び出して、移動への呼び出しを保存しますコンストラクタ。
std::string
の移動コンストラクターは大したことではないかもしれませんが、emplace
関数は、オブジェクトに効率的な移動コンストラクターがない場合に保存でき、クラス型に削除された移動がある場合でも使用できます何らかの理由でコンストラクタ。 (OK、std::vector
はmoveコンストラクタとcopyコンストラクタを削除すると満足できませんが、他のコンテナはそれで問題ありません。)
最初に明確にします:
emplace
ファミリーはargumentsをオブジェクトではなくコンストラクタに受け入れます自身。
次に、それらの引数を使用してオブジェクトを代わりに構築します。一時オブジェクトを構築してから、それをコンテナにコピーまたは移動することはありません。
つまり、引数として同じ型のオブジェクトを取ることは、コピーと移動の構築が行うこととまったく同じです。そのため、例では、それらは同じコンストラクタを呼び出します。それらは、すでに構築されたstring
で呼び出されます。
emplace
とPush
が完全に異なる場合は、emplace
が、オブジェクト自体ではないコンストラクター引数を使用して呼び出されます。emplace
は一時オブジェクトを構築する必要はありません。 そしてコンテナにコピーします。
std::vector<std::string> strvec;
strvec.emplace_back("abc") //calls string(const char*)
strvec.Push_back("abc") //is equivalent to...
strvec.Push_back(string("abc")) //which calls string(const char*) then string(string&&)