web-dev-qa-db-ja.com

std :: vector :: emplace_backおよびstd :: move

std::vector::emplace_backstd::moveを一緒に使用する利点はありますか?または、std::vector::emplace_backがインプレース構築を行うため、冗長です。

説明のためのケース:

std::vector<std::string> bar;

最初:

bar.emplace_back(std::move(std::string("some_string")));

2番目:

std::string str("some_string");
bar.emplace_back(std::move(str));

番目:

bar.emplace_back(std::move("some_string"));
24
Humam Helfawi

2番目のバージョンでは、利点があります。 emplace_backを使用すると、std::stringのmoveコンストラクターが呼び出され、std::moveを使用するとコピーを保存できます(その文字列がSSOバッファーに格納されていない場合)。これは、この場合のPush_backと本質的に同じであることに注意してください。

文字列はすでにprvalueであるため、最初のバージョンのstd::moveは不要です。

文字列リテラルは移動できないため、3番目のバージョンのstd::moveは無関係です。

最も単純で最も効率的な方法は次のとおりです。

bar.emplace_back("some_string");

リテラルはコンストラクタに完全に転送されるため、不要なstd::string構成は必要ありません。

25
TartanLlama

emplace_backのような呼び出し

new (data+size) T(std::forward<Args>(args)...);

argsが基本-右辺値を参照しないstd::stringの場合、式は次のようにコンパイルされます

new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)

コピーコンストラクタが行われることを意味します。ただし、strstd::moveを使用すると、コードは次のようにコンパイルされます。

new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)

したがって、移動セマンティクスが行われます。これは大きなパフォーマンスの向上です。
注意してください。strは左辺値であり、名前が付いているため、そこからr値参照を作成するには、std::moveを使用する必要があります。

例では

vec.emplace_back("some literal"); 

コードはコンパイルされます

new (data+size) std::string("literal"); //calls std::string::string(const char*);

一時的なものはありません。

3番目の例はナンセンスです。リテラルを移動することはできません。

6
David Haim

emplace_backの全体的な考え方は、コピーおよび移動操作を取り除くことです。 std::stringの入力パラメーターをemplace_backに渡すだけです。 std::stringオブジェクトは、emplace_backメソッド内で構築されます。

bar.emplace_back("some_string");

すでに文字列がある場合は、std::moveを使用するのが理にかなっています。 strからデータを移動することにより、std::stringオブジェクトがemplace_back内に構築されます。

std::string str("some_string");
bar.emplace_back(std::move(str));
4
Stas

2番目のケースではそうする意味があります。このコードを考えてみましょう:

int main()
{
    std::vector<std::string> bar;
    std::string str("some_string");
    bar.emplace_back(std::move(str)); str.clear();
    // bar.emplace_back(str);
    std::cout << str << std::endl;
}

コメントを上の行に変更すると、「some_string」の2つのコピー(barに1つとstrに1つ)が作成されることがわかります。だからそれは何かを変えます。

それ以外の場合、1つ目は一時ファイルを移動し、3つ目は定数文字列リテラルを移動します。それは何もしません。

1
Ami Tavory