続:lambda式を直接適用可能なtransform_iterator

ということで,実装しました.そもそも何故こんなに苦労したかというと,

  • 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_functor lambda_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_adaptor 
  functor_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