C++11 で rvalue reference による perfect forwarding が実現されたため、STL コンテナに emplace 系の関数が追加されている。insert() や push_back() は value_type 型の値自体を与える必要があるが、emplace 系には生成に必要な引数(コンストラクタの引数等)を渡してやれば良い。
std::vector<std::pair<int, int>> v1; std::pair<int, int> p{ 0, 0 }; v1.push_back(p); v1.push_back({ 0, 0 }); v1.emplace_back(0, 0);
これで無駄な temporary 作成が無くなり万々歳、なのだが。非常によく似た以下の例は実はコンパイルできない。
struct point { int x, y; }; std::vector<point> v2; point pt{ 0, 0 }; v2.push_back(pt); v2.push_back({ 0, 0 }); // (g++ 4.5 compilation error // by ambiguous between const value_type & and value_type &&) v2.emplace_back(0, 0); // g++ 4.5-4.8 compilation error // new initializer expression list treated as compound expression
g++ 4.5 特有のエラーについては置いとくとして、下側なんでやねんというのは StackOverflow の質問 ならびに引用されている DR を見てもらえれば良いのだが、問題は emplace 系で呼び出される std::allocator_traits::construct(m, p, args) が最終的に ::new((void *)p) U(std::forward
std::vector<std::vector<int>> v; v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization
is_constructible<U, Args...> が true かどうか(direct-initialization が有効かどうか)によって、direct-initialization か list-initialization かを呼び分ける方向での修正が提案されている。
こんな単純そうな例ですら落とし穴があるとは、ほんと C++11 難しい。
0 件のコメント:
コメントを投稿