1. ホーム
  2. c++

[解決済み】なぜemplace_backの代わりにpush_backを使うことがあるのでしょうか?

2022-04-03 04:15:56

質問

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_backemplace_back .

もし、このコードをより安全な push_back しかし、私はこのバグを隠してしまい、数ヵ月後にようやく発見しました。