[解決済み】なぜemplace_backの代わりにpush_backを使うことがあるのでしょうか?
質問
C++11のベクトルには、新しい関数
emplace_back
. とは異なり
push_back
は、コピーを避けるためにコンパイラの最適化に依存しています。
emplace_back
は完全な転送を使用して、オブジェクトをインプレースで作成するためにコンストラクタに直接引数を送信します。私には
emplace_back
はすべて
push_back
ができるのですが、ある時はより良く(しかし決して悪くならない)できるのです。
を使用しなければならない理由は何ですか?
push_back
?
解決方法は?
この質問について、私はこの4年間、かなり考え込んでしまいました。その結果、以下のような結論に達しました。
push_back
vs.
emplace_back
は、全体像が見えません。
昨年、C++Nowで、次のようなプレゼンテーションを行いました。
C++14における型推論
. 私は、まず
push_back
vs.
emplace_back
の13:49のところですが、それ以前にも裏付けとなる有用な情報があります。
本当の第一の違いは、暗黙的なコンストラクタと明示的なコンストラクタに関係するものです。に渡したい引数が 1 つだけある場合を考えてみましょう。
push_back
または
emplace_back
.
std::vector<T> v;
v.push_back(x);
v.emplace_back(x);
最適化コンパイラが手を入れた後では、生成されるコードという点では、この2つの文に違いはありません。従来の常識では
push_back
は一時的なオブジェクトを構築し、それを
v
一方
emplace_back
は、引数を転送し、コピーや移動なしにその場で直接構築します。これは、標準ライブラリに書かれているコードからすると正しいかもしれませんが、最適化コンパイラの仕事は、あなたが書いたコードを生成することであるという誤った前提に立っています。最適化コンパイラの仕事は、あなたがプラットフォーム固有の最適化の専門家であり、保守性を気にせず性能だけを追求するのであれば、あなたが書いたであろうコードを生成することなのです。
この2つの文の実際の違いは、より強力な
emplace_back
はあらゆる種類のコンストラクタを呼び出しますが、より慎重な
push_back
は、暗黙のコンストラクタのみを呼び出します。暗黙のコンストラクタは安全であることが前提です。もし暗黙のうちに
U
から
T
ということですね。
U
のすべての情報を保持することができます。
T
を損失なく使用することができます。を渡すのは、ほとんどどんな状況でも安全です。
T
にしておけば、誰も気にしないでしょう。
U
の代わりに 暗黙のコンストラクタの良い例として
std::uint32_t
から
std::uint64_t
. 暗黙の変換の悪い例として
double
から
std::uint8_t
.
プログラミングには慎重でありたいものです。強力な機能を使えば使うほど、誤って不正なことや予期せぬことをしやすくなるからです。もし、明示的なコンストラクタを呼び出すつもりであれば、その強力な
emplace_back
. 暗黙のコンストラクタだけを呼び出したい場合は、安全性の高い
push_back
.
例
std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile
std::unique_ptr<T>
の明示的なコンストラクタがあります。
T *
. なぜなら
emplace_back
は明示的なコンストラクタを呼び出すことができるので、所有者でないポインタを渡しても問題なくコンパイルできます。しかし
v
がスコープ外に出た場合、デストラクタは
delete
で割り当てられていないそのポインタで、そのポインタは
new
は単なるスタックオブジェクトだからです。このため、未定義の動作になります。
これは単なる創作コードではありません。これは私が実際に遭遇した制作上のバグです。そのコードは
std::vector<T *>
しかし、その中身は自分のものでした。C++11への移行の一環として、私は正しく
T *
を
std::unique_ptr<T>
のように、ベクターがそのメモリを所有していることを示すようにしました。しかし、この変更は2012年当時の私の理解に基づいており、その当時は、"
emplace_back
はすべてを行う
push_back
はそれ以上のことができるのに、なぜ私が
push_back
?"であったので、私も
push_back
に
emplace_back
.
もし、このコードをより安全な
push_back
しかし、私はこのバグを隠してしまい、数ヵ月後にようやく発見しました。
関連
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] push_back vs emplace_back
-
[解決済み] なぜ (int)x ではなく static_cast<int>(x) を使うのですか?
-
[解決済み] noexceptを本当に使うべきはいつですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
-
[解決済み】なぜC++プログラマは'new'の使用を最小限に抑えなければならないのでしょうか?
-
[解決済み】モダンC++はタダで性能を手に入れられる?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 != と =! の違いと例(C++の場合)
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み] push_back vs emplace_back
-
[解決済み】C++11を有効にするとstd::vectorのパフォーマンスが低下する件