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 件のコメント:
コメントを投稿