理解が浅いので間違っている可能性がある。
前提として、std::vectorのpush_backとemplace_backの定義が重要となるので、下記に記載する。
void push_back(const T& x); // (1) void push_back(T&& x); // (2) C++11
(2)のxは右辺値参照。
template <class... Args> void emplace_back(Args&&... args); // (1) C++11 template <class... Args> reference emplace_back(Args&&... args); // (1) C++17 template <class... Args> void vector<bool>::emplace_back(Args&&... args); // (2) C++11 template <class... Args> reference vector<bool>::emplace_back(Args&&... args); // (2) C++17
テンプレート関数の仮引数なので、argsはすべてユニヴァーサル参照。
struct Hoge final { public: // デフォルトコンストラクタ Hoge() = default; // コピーコンストラクタ Hoge(const Hoge& other){ cout << "コピーコンストラクタ" << endl; this->a = other.a; } // ムーブコンストラクタ Hoge(Hoge&& other){ cout << "ムーブコンストラクタ" << endl; this->a = other.a; } // コピー代入演算子オーバーロード Hoge& operator=(const Hoge& obj){ cout << "コピー演算子オーバーロード" << endl; this->a = obj.a; return *this; } // ムーブ代入演算子オーバーロード Hoge& operator=(Hoge&& obj){ cout << "ムーブ演算子オーバーロード" << endl; this->a = obj.a; return *this; } int a; };
という構造体があったとして、
Hoge a{}; std::vector<Hoge> v{}; v.push_back(a);
まずはpush_back(T)。
実引数として左辺値が渡されているため、「void push_back(const T& x)」が呼び出され、実行すると、「コピーコンストラクタ」と表示される。
上記は内部的に仮引数xのコピーをコピーコンストラクタで生成して、vに要素を追加するということらしい。実引数aと仮引数xの間は参照渡しなので、インスタンス生成は行われない(はず)。
コピーしているので、emplace_back後に、aの内容を修正しても、v内の要素は変化しない。
「コピー演算時オーバーロード」は出力されないので、コピー代入演算子をつかってコピーしているわけではない模様。
Hoge a{}; std::vector<Hoge> v{}; v.push_back(std::move(a));
push_back(std::move(T))。
実引数aがstd::moveで右辺値として渡されているため、「void push_back(T&& x)」が呼び出され、実行すると、「ムーブコンストラクタ」と表示される。
内部的には、一時オブジェクトx(実質的にaと同じ?)をムーブコンストラクタで、ムーブして、vに要素を追加する。
Hoge a{}; std::vector<Hoge> v{}; v.emplace_back(a);
次に、emplace_back(T)。
実行すると、「コピーコンストラクタ」と表示される。
実引数aが左辺値として渡され、仮引数argsがユニヴァーサル参照となる「template reference emplace_back(Args&&… args)」が呼び出される(C++17の場合)。
これは、内部的に実引数aからコピーコンストラクタを呼び出して、直接vに要素を追加するということらしい。
なので、push_back(T)より、一時的なインスタンスからのコピーが省略されるため速いということらしい。
なお、結局コピーしているので、emplace_back後に、aの内容を修正しても、v内の要素は変化しない。
実引数が左辺値なので、型推論で、仮引数argsが左辺値として解釈されるということだろうか?
※いまいちわかっていないので間違っているかも。詳細がわかったら追記。
Hoge a{}; std::vector<Hoge> v{}; v.emplace_back(std::move(a));
最後に、emplace_back(std::move(T))。
実行すると、「ムーブコンストラクタ」と表示される。
実引数aがstd::moveで右辺値として渡され、仮引数argsがユニヴァーサル参照となる「template reference emplace_back(Args&&… args)」が呼び出される(C++17の場合)。
内部的には実引数aからムーブコンストラクタを呼び出して、直接vに要素を追加するということらしい。
実行結果は、emplace_back(T)と変わらず、emplace_back後に、aの内容を修正しても、v内の要素は変化しない。
ただし、ムーブ演算が実行されているのは確実なので、Hogeが内部的にスマートポインタを持っていたとしたら、挙動が変わる気がする。
この辺は継続調査したいところ。
実引数が右辺値なので、型推論で、仮引数argsが右辺値として解釈されるということだろうか?
※いまいちわかっていないので間違っているかも。詳細がわかったら追記。