C++11にはrange based forが加わり,rangeに対する走査処理を書くのが楽になりました.しかしながら,このstd::pair<Iterator, Iterator>で表されるIterator対はこのrangeには含まれません*1. そのため以下のようなコードはコンパイルエラーになります.
#include <iostream> #include <map> int main() { auto const map = std::multimap<int, int>{{2, 3}, {2, 4}, {2, 5}}; for (auto const& entry : map.equal_range(2)) { // equal_rangeはIterator対を返す!! std::cout << "key: " << entry.first << " value: " << entry.second << std::endl; } }
こういった場合は,Boost.Rangeのfor_eachとlambda式を組み合わせることで解決できます.
#include <iostream> #include <map> #include <boost/range/algorithm/for_each.hpp> int main() { auto const map = std::multimap<int, int>{{2, 3}, {2, 4}, {2, 5}}; boost::for_each(map.equal_range(2), [](std::pair<int, int> const& entry) { std::cout << "key: " << entry.first << " value: " << entry.second << std::endl; }); }
しかしながら,lambda式の引数の型名が長くなってくると,このやり方は少々面倒なことになります.特にBoost.Graphではそれが顕著になります.
#include <iostream> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/graph_traits.hpp> #include <boost/range/algorithm/for_each.hpp> int main() { boost::adjacency_list<> graph; // lambda式の引数の型名が長い boost::for_each(vertices(graph), [&](boost::graph_traits<decltype(graph)>::vertex_descriptor const v) { std::cout << "v index: " << get(boost::vertex_index, graph, v) << std::endl; }); }
こうなってくると,range based forを通した型推論を使って型名を書くのを避けたくなります.しかしながら,既に述べた通りstd::pairによるIterator対を使うとコンパイルエラーになってしまうため,そのままでは使用できません.自分でstd::pairに対するwrapperを書いてもいいですが,Boost.Rangeにiterator_rangeがあるのでこれを使用します.
#include <iostream> #include <boost/graph/adjacency_list.hpp> #include <boost/range/iterator_range.hpp> #include <boost/range/algorithm/for_each.hpp> int main() { boost::adjacency_list<> graph; // autoでOK!! for (auto v : boost::make_iterator_range(vertices(graph))) { std::cout << "v index: " << get(boost::vertex_index, graph, v) << std::endl; }; }
make_iterator_rangeはfiltered等のadaptorとは異なり(adaptorもiterator_rangeを使ってました),Iteratorをコピーするので元のIterator対の寿命を気にする必要がありません(上記ではvertices(graph)が返すIterator対の寿命)*2.
まとめ
Generic lambdaサイコー!!