ある Generator を受けて構築されて, operator* の結果が Generator の operator() の結果を返すようなイテレータ(
もしくは Range)って実装が意外と難しいよね,ってゆ〜.
typedef boost::mt19937 rng_type; rng_type rng; typedef boost::uniform_real<> dist_type; dist_type dist( 0.0, 1.0 ); boost::variate_generator< rng_type &, dist_type > rand( rng, dist ); generating_iterator it( rand ); *it; // rand の値を吐く ++it; *it; // 次の rand の値を吐く
boost::iterator_facade 使って dereference() の実装で operator() 呼べば良いように一見思えるけれど, ReadableIterator の要求としてイテレータのオブジェクトを動かしてない限り operator* では同じ値を吐かないといけない
If a == b then *a is equivalent to *b.
から, dereference() で operator() を呼ぶ実装だと
*it; *it; // 前と違う値を吐く
となってまずいよねってゆ〜.したがって必然的に operator++ (boost::iterator_facade を使っているなら increment)が呼び出されたタイミングで保持している Generator の operator() を呼び出し,返ってきた値を保持して, opreator* でその保持している値を返すっつー実装に限定されるよねってゆ〜.
generating_iterator it( rand ); // ここで rand を評価して値を取って保持する *it; // 保持している値を返すだけ ++it; // ここで rand を評価して新しい値を取って保持する *it; // 保持している値を返すだけ *it; // 前と同じ値が返ってくる(ReadableIterator の要求を満足する)
ここまでは,だいたい std::istream[buf]_iterator の類推からまぁ O.K. な気がするけれど,1つだけ
generating_iterator it( rand ); // ここで rand を評価して値を取って保持する // 以降 it を全く使わない
という構文を考えると,これ it を全く使っていないにも関わらず Generator が1回評価されちゃうよねってゆ〜.
ここまでの議論だと「そこまでこだわらなくてもええんちゃうの?」って言われそうだけれど,これにこだわる必要がどうしても出てくるもう1つ別の例を考えてみる.
generating_n_iterator it( rand, 100 ); // rand を 100 回評価したら past-the-end (終了位置)になる for( ; it != generating_n_iterator(); ++it ){ std::cout << *it << std::endl; }
こういう
generating_n_iterator()
をどう実装するかものすごい難しいよねってゆ〜.最も簡単な方法は, iterator がポインタを保持していて, rand を評価した結果を free-store 上にコピーする,で past-the-end 状態はそのポインタを NULL として表現するっちぅ方策だけれど
generating_n_iterator( rand, 100 ); // ここで rand を評価して