トップページ > コンテナとイテレータ >
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <boost/range.hpp>
using namespace std;
using namespace boost;
// Rangeの全ての要素を表示
template<typename Range>
void print( Range& r )
{
typename
range_const_iterator<Range>::type i = begin(r), e = end(r);
for(; i!=e; ++i)
cout << *i << endl;
}
// 補助関数(10進の桁数の大小を比較)
bool cmp_digits( int a, int b )
{ return (int)log10((double)a) < (int)log10((double)b); }
// 2ケタの範囲を表示
int main()
{
int sorted[] = {1,2,3,45,67,89,123,456,7890};
print( sorted );
cout << endl;
print( std::equal_range(begin(sorted), end(sorted), 99, &cmp_digits) );
}
1 2 3 45 67 89 123 456 7890 45 67 89
STLのアルゴリズムは「イテレータ」という、データ列中の位置、一点を指す概念を元に設計されています。 「ここからここまで」のような「範囲」を表現したければ、2個のイテレータを使って表すことになります。 そうじゃなくて、直接「範囲」を表すオブジェクトというのを考えてみたらどうでしょう?というのが、 Rangeライブラリの提案です。
上のコードは、「2個のイテレータ」では綺麗に表せないけれど、 「範囲」として記述すれば綺麗に書ける例としてあげてみました。 print関数テンプレートは、「範囲」を受け取ってその要素を先頭から順番に標準出力に表示してます。 boost/range.hpp ヘッダを読み込むと、配列やSTLのコンテナ、イテレータのstd::pairなどに begin/end関数が適用できて、イテレータが取得できるのようになってますので、print(sorted) で配列の中身が全部表示できます。
STL的な書き方だと、このprint関数は print(Iterator beg, Iterator end) と2つイテレータを取る形で定義するのが普通だと思います。でもこれを「範囲」を受け取るように定義すると、 色々便利になります…!というのが次の行です。 std::equal_rangeはstd::pair<iterator,iterator>というイテレータの組を返値として返しますが、 イテレータのpairは「範囲」として扱えるので、そのままprintの引数にできます。 もしprintがiterator2つを引数にとるのだったら、equal_rangeの返値をいったん変数に保存して print(p.first,p.second)という不格好なコードを書かなければなりません。
Boost.Range自体はRangeを扱うインターフェイス(begin/end関数とrange_iteratorテンプレート)を決めて、 標準のコンテナ他1くつかをRangeとして扱えるように定義しているだけの小さなライブラリです。
実際にRangeの威力を発揮するには、Rangeをフル活用したアルゴリズムライブラリが不可欠です。 既にいくつかRangeベースのライブラリが登場しています。