push_backとemplace_back


761

私は少しの違いについて混乱しているpush_backemplace_back

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

push_back右辺値参照を取得するオーバーロードがあるので、目的emplace_backがどうなるかよくわかりませんか?



16
(トーマスが下で言うように)問題のコードはC ++ 0xのMSVSのエミュレーションからのものであり、実際のC ++ 0xのものではないことに注意してください。
me22

5
読みやすい論文は、open-std.org / jtc1 / sc22 / wg21 / docs / papers / 2007 / n2345.pdfです。N2642は主に規格を表す言葉です。N2345は、アイデアを説明し、動機付けする論文です。
アラン

MSVC10でさえ、単一引数コンストラクターへの完全な転送を提供template <class _Valty> void emplace_back(_Valty&& _Val)する汎用参照を取得するバージョンがあることに注意してくださいexplicit
joki 2017

関連:push_backより好ましいケースはありますemplace_backか?私が考えることができる唯一のケースは、クラスが何らかの形でコピー可能(T&operator=(constT&))であるが、構築可能()ではなかっT(constT&)た場合ですが、なぜそれが望まれるのかは考えられません。
ベン

回答:


568

訪問者が言ったことに加えて:

void emplace_back(Type&& _Val)MSCV10が提供する機能は、厳密にはと同等であるため、非準拠で冗長ですpush_back(Type&& _Val)

しかし、実際のC ++ 0x形式emplace_backは非常に便利ですvoid emplace_back(Args&&...)

を受け取る代わりにvalue_type、引数の可変リストを受け取ります。つまり、引数を完全に転送し、一時オブジェクトをまったく使用せずにオブジェクトを直接コンテナーに構築できるようになります。

RVOと移動のセマンティックがどれだけ巧妙にテーブルにもたらされても、push_backが不要なコピー(または移動)を作成する可能性が高い複雑なケースがあるため、これは便利です。たとえば、の従来のinsert()関数ではstd::map、一時ファイルを作成する必要があります。一時ファイルは次ににコピーされ、std::pair<Key, Value>次にマップにコピーされます。

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";

// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); 

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

では、MSVCに正しいバージョンのemplace_backを実装しなかったのはなぜですか?実際、かなり前からバグがあったため、Visual C ++ブログで同じ質問をしました。MicrosoftのVisual C ++標準ライブラリ実装の公式メンテナであるStephan T Lavavejからの回答です。

Q:現在、ベータ2エンプレース機能は、ある種のプレースホルダーにすぎませんか?

A:ご存知かもしれませんが、可変テンプレートはVC10に実装されていません。make_shared<T>()、タプル、およびの新しいものなどのプリプロセッサマシンでそれらをシミュレートします <functional>。このプリプロセッサー機構は、使用および保守が比較的困難です。また、サブヘッダーを繰り返し含める必要があるため、コンパイル速度に大きく影響します。時間の制約とコンパイル速度の懸念の組み合わせにより、私たちはemplace関数で可変テンプレートをシミュレートしていません。

可変個のテンプレートがコンパイラーに実装されている場合は、emplace関数を含むライブラリーでそれらを利用することが期待できます。私たちは適合性を非常に真剣に受け止めていますが、残念ながら、すべてを一度にすべて行うことはできません。

それは理解できる決断です。プリプロセッサの恐ろしいトリックを使って可変テンプレートをエミュレートすることを一度だけ試みた人は誰でも、これがいかに嫌なことかを知っています。


101
ここでは、C ++の問題ではなく、MSVS10の問題であることを明確にすることが最も重要な部分です。ありがとう。
me22

11
C ++コードの最後の行は機能しないと思います。pair<const int,Complicated>int、別のint、double、および4番目のパラメータとして文字列をとるコンストラクタはありません。ただし、ピース単位のコンストラクターを使用して、このペアオブジェクトを直接作成できます。もちろん、構文は異なります:m.emplace(std::piecewise,std::forward_as_tuple(4),std::forward_as_tuple(anInt,aDouble,aString));
sellibitze

3
幸いにも、可変テンプレートがVS2013に含まれ、プレビューされます。
Daniel Earwicker 2013

11
この回答は、vs2013の新しい開発を反映するように更新する必要がありますか?
becko 2014

6
現在 Visual Studio 2013以降を使用している場合はemplace_back、可変テンプレートが追加されたときにVisual C ++に実装されている限り、「実」のサポートが必要です:msdn.microsoft.com/en-us/library/hh567368。 aspx
kayleeFrye_onDeck 2017年

200

emplace_back型の引数をとるべきではありませんが、vector::value_type代わりに、追加されたアイテムのコンストラクターに転送される可変引数をとります。

template <class... Args> void emplace_back(Args&&... args); 

value_typeコピーコンストラクターに転送されるaを渡すことが可能です。

これは引数を転送するため、右辺値がない場合でも、コンテナーは移動されたコピーではなく「コピーされた」コピーを格納することを意味します。

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

しかし、上記は何をすべきかと同じでなければなりpush_backません。おそらく、次のようなユースケースを対象としています。

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings

2
@David:しかし、あなたはsスコープ内に移動しました、それは危険ではないですか?
Matthieu M.

2
その値のためにsを使用する予定がなくても危険ではありません。移動してもsは無効になりません。移動では、sで既に行われた内部メモリ割り当てのみが盗まれ、デフォルトの状態(スティングが割り当てられていない状態)のままになります。
デビッド

4
@David:移動されたオブジェクトが、その後の破棄以外の用途で有効である必要があるかどうかはわかりません。
Ben Voigt

46
vec.emplace_back("Hello")以来、動作しますconst char*引数がされる転送stringコンストラクタ。これがのポイントですemplace_back
Alexandre C.

8
@BenVoigt:移動元のオブジェクトは、有効な(ただし指定されていない)状態である必要があります。ただし、これは必ずしもそれに対して何らかの操作を実行できるという意味ではありません。考えてくださいstd::vector。空std::vectorは有効な状態ですが、呼び出すことはできませんfront()。つまり、前提条件のない関数を呼び出すことができます(デストラクタに前提条件を設定することはできません)。
David Stone、

96

の最適化をemplace_back次の例で示します。

以下のためにemplace_back、コンストラクタA (int x_arg)と呼ばれます。そしてfor push_back A (int x_arg)が最初にmove A (A &&rhs)呼び出され、その後に呼び出されます。

もちろん、コンストラクタはとしてマークする必要がありますexplicitが、現在の例では明示性を削除するのに適しています。

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

出力:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)

21
emplace_backvsを呼び出したときに実際に何が起こるかを示すコード例の+1 push_back
Shawn

v.emplace_back(x);xが明示的に移動構築可能で、明示的にコピー構築可能である場所を呼び出すコードがあることに気付いた後、私はここに来ました。emplace_back「暗黙的に」明示的であるという事実は、追加のための私の頼りになる機能はおそらくであるはずだと私に思わせますpush_back。考え?
ベン

a.emplace_back2回目に呼び出すと、移動コンストラクタが呼び出されます。
XÆA-12


8

emplace_back準拠する実装はvector<Object>::value_type、ベクトルに追加されたときに引数をコンストラクターに転送します。Visual Studioは可変長テンプレートをサポートしていませんが、可変長テンプレートはVisual Studio 2013 RCでサポートされるので、準拠する署名が追加されると思います。

を使用するemplace_backと、引数をvector<Object>::value_typeコンストラクタに直接転送する場合emplace_back、厳密に言えば、関数に対して型を移動可能またはコピー可能にする必要はありません。このvector<NonCopyableNonMovableObject>場合、vector<Object>::value_type 成長するためにコピー可能または移動可能なタイプが必要になるため、これは役に立ちません。

しかし、ノート、これはのために役立つことができることをstd::map<Key, NonCopyableNonMovableObject>あなたがマップ内のエントリを割り当てると、それが移動したりとは異なり、もはやこれまでにコピーする必要がないので、vectorあなたが使用できることを意味し、std::mapどちらもコピー可能であるマッピングされたタイプと効果的にも可動。


8

リストの場合はもう1つ:

// constructs the elements in place.                                                
emplace_back("element");


//It will create new object and then copy(or move) its value of arguments.
push_back(explicitDataType{"element"});

1

の具体的な使用例emplace_back:コンテナーにプッシュされる一時オブジェクトを作成する必要がある場合emplace_backは、push_back。コンテナ内にオブジェクトを配置します。

ノート:

  1. push_back上記の場合、一時オブジェクトが作成され、コンテナに移動されます。ただし、で使用されるインプレース構築emplace_backは、オブジェクトを構築して移動するよりもパフォーマンスが高くなります(これには通常、コピーが含まれます)。
  2. 一般的に、すべてのケースのemplace_back代わりにpush_back、ほとんど問題なく使用できます。(例外を参照)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.