ということで,実装しました.そもそも何故こんなに苦労したかというと,
- transform_iteratorに渡すfunctorは戻り値が定まっている必要がある
- 一方で,lambdaはoperator()に引数が渡される瞬間まで戻り値が決定しない
というジレンマがあったせいで,これをどうにかしようと色々模索しました.で,lambdaは確かに戻り値が不定だがtransform_iteratorを生成するときにはtransform_iteratorのbaseとなるiteratorが返す値の型は一定なのだからlambdaに与える引数の型,そしてそれから推論される戻り値の型は一定に決定できるだろう,ということでtransform_iteratorのobeject generatorでid:Cryolite:20040518で作ったadaptorをlambdaに噛ますことにしました.
で,出来たコードが以下.
template<class T, class Iterator> boost::transform_iterator< unary_adaptor< boost::lambda::lambda_functor<T>, typename std::iterator_traits<Iterator>::value_type, typename boost::lambda::lambda_functor<T>::inherited ::template sig<boost::tuples::tuple< typename std::iterator_traits<Iterator>::reference> >::type>, Iterator> make_transform_iterator(Iterator it, boost::lambda::lambda_functor<T> fun) { typedef typename boost::lambda::lambda_functor<T> lambda_type; typedef typename std::iterator_traits<Iterator>::value_type argument_type; typedef typename boost::lambda::lambda_functor<T>::inherited ::template sig< boost::tuples::tuple< typename std::iterator_traits<Iterator>::reference > >::type result_type; typedef unary_adaptor<lambda_type, argument_type, result_type> functor_type; return boost::transform_iterator<functor_type, Iterator>(it, functor_type(fun)); }
何か途方も無く得体の知れないコードが出来てしまいましたw.が,やっていることは非常に単純で,単にmake_transform_iteratorでfunctorがlambda式の場合のpartial specializationをやっているだけです.戻り値が複雑なのはtypedefが出来ないせいなので,コード本体の中でいくつか注目すべき点を挙げていきます.
typedef typename boost::lambda::lambda_functorlambda_type;
lambda_functorはlambda式が生成するfunctorです.Tには式テンプレートの型がずらずら並ぶことになります.
typedef typename std::iterator_traits::value_type argument_type;
Iteratorにはtransform_iteratorのbaseとなる(変換の対象となる)iteratorの型が入ります.これにiterator_traitsを適用してlambda式に引数として渡される型を得ます.
typedef typename boost::lambda::lambda_functor::inherited ::template sig< boost::tuples::tuple< typename std::iterator_traits ::reference > >::type result_type;
ここが恐らく一番中心的な部分で,自分もこの部分を得るためのlambdaのコードの解析に多少時間を取られてしまいました.まず,lambdaは引数をtupleで持っているのが一点.で,sigというのは引数の型の情報を持つtupleから戻り値の型推論を行うための補助的な構造体です・・・多分w.publicで開示されているので,多分使って良いのでしょう・・・良いのか?
typedef unary_adaptorfunctor_type; return boost::transform_iterator (it, functor_type(fun));
後はid:Cryolite:20040518で指摘したとおり,adaptしてしまえばtransform_iteratorの変換関数としてlambdaが使えるという寸法です.
終わり.
以上のことを可能にするヘッダlambda_transform_iterator.hppを置いておきます.念のためにpartial specializationをcryという変な名前空間においてますが,多分boost名前空間に追加しても問題ないと思います.無保証なのでお遊び程度に使ってやってください.(個人的には色々使い方があると思っているのですが)
ちなみに,上はobject generatorだけ提供しているので名前付きでlambda + transform_iteratorをやる(要するにobject generator経由ではなくて,普通のコンストラクタ経由で生成して変数として使いまわす)ことは考慮していません.ただこれには理由があって,lambdaは式テンプレートなのでlambdaで生成されるfunctorの型をexplicitに書かないといけないという式テンプレート特有(かつ型推論のない現在のC++特有)の問題があるからです.っていうかtypeofの早期実現を!!(高速道路誘致を希望する地元住民風に)
以下,サンプル.
#include#include #include #include #include #include #include #include "lambda_transform_iterator.hpp" int main(int argc, char *argv[]) { using namespace std; using namespace boost; using namespace boost::lambda; using namespace cry; // 60未満の2の倍数または3の倍数を取る. // ただし,6の倍数は取らない. set_symmetric_difference( make_transform_iterator( make_counting_iterator(0), _1 * 2), make_transform_iterator( make_counting_iterator(60 / 2), _1 * 2), make_transform_iterator( make_counting_iterator(0), _1 * 3), make_transform_iterator( make_counting_iterator(60 / 3), _1 * 3), ostream_iterator (cout, "\n"), less ()); return 0; }
#2004/09/30追記
http://d.hatena.ne.jp/Cryolite/20040930#p1
に上のmake_transform_iteratorの改訂版を提示しました.上のmake_transform_iteratorは色々間違っているので改訂版の方を参照してください.
2004-05-19