すでに構築されたオブジェクトに対するstd :: moveとemplace_back()を使用したC ++ 11 push_back()の効率


85

C ++ 11では、インプレース構築が可能であるemplace_back()ため、(効率の観点から)一般的に好まれますが、すでに構築されたオブジェクトで使用する場合も同様ですか?push_back()push_back(std::move())

たとえばemplace_back()、次のような場合でもまだ好まれますか?

std::string mystring("hello world");
std::vector<std::string> myvector;

myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)

さらに、上記の例には、代わりに次のような利点がありますか。

myvector.emplace_back(std::move(mystring));

または、ここでの移動は完全に冗長ですか、それとも効果がありませんか?


myvector.emplace_back(mystring);コピーして移動しません。他の2つの動きは、同等である必要があります。
TC

この質問と結果も参照してください:挿入と配置に関するVC ++の要求された調査
ildjarn 2014

回答:


115

あなたが提供したさまざまな呼び出しが何をするか見てみましょう:

  1. emplace_back(mystring):これは、指定した引数を使用した新しい要素のインプレース構造です。左辺値を指定したので、そのインプレース構築は実際にはコピー構築です。つまり、これは呼び出しと同じです。push_back(mystring)

  2. push_back(std::move(mystring)):これはmove-insertionを呼び出します。これは、std :: stringの場合はインプレースmove-constructionです。

  3. emplace_back(std::move(mystring)):これも、指定した引数を使用したインプレース構造です。その引数は右辺値であるため、のmove-constructorを呼び出しますstd::string。つまり、2のようなインプレースmove-constructionです。

言い換えると、タイプTの1つの引数で呼び出された場合、それは右辺値または左辺値でemplace_backありpush_back、同等です。

ただし、他の引数emplace_backについては、たとえば:char const*内のaを使用してレースに勝ちvector<string>ます。

  1. emplace_back("foo")通話string::string(char const*)インプレース・建設のため。

  2. push_back("foo")最初にstring::string(char const*)関数のシグネチャと一致するために必要な暗黙の変換を呼び出す必要があり、次に上記のケース2のようなmove-insertionを呼び出す必要があります。したがって、それはと同等ですpush_back(string("foo"))


1
移動コンストラクターは一般にコピーコンストラクターよりも効率的であるため、同じセマンティクスにもかかわらず、右辺値(ケース2、3)を使用する方が左辺値(ケース1)を使用するよりも効率的です。
ライアンリー

そのような状況はどうですか?void foo(string && s){vector.emplace(s); // 1 vector.emplace(std :: move(s)); // 2}
VALOD9 2018年

@VALODこれらは私のリストの1番と3番です。s右辺結合することのみ右辺値への参照が、内部として定義することができるfoos左辺値です。
Arne Mertz

1

emplace_backは、右辺値参照のリストを取得し、その場で直接コンテナー要素を構築しようとします。コンテナ要素コンストラクタがサポートするすべての型でemplace_backを呼び出すことができます。右辺値参照ではないパラメーターに対してemplace_backを呼び出すと、通常の参照に「フォールバック」し、パラメーターとコンテナー要素が同じタイプの場合、少なくともコピーコンストラクターは呼び出されません。あなたの場合、コンパイラはパラメータmyvectorが移動可能であることを認識できなかったため、 'myvector.emplace_back(mystring)'は文字列のコピーを作成する必要があります。したがって、std :: moveを挿入して、目的のメリットを得ることができます。push_backは、すでに構築されている要素のemplace_backと同様に機能するはずです。


2
これは、転送/ユニバーサル参照を説明するための興味深い方法です。
TC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.