boost user - functional

since: 2002-08-28 update: 2002-08-28 count:

STLの functional には、mem_fun*, bind* という、 まあ STL内では割と難しめの関数アダプタが入っています。 それをここで説明する気はありません。 知らなければ Effective STL あたりを購入して下さい。

STLのこれらにはちょっと嫌な仕様が二つあります。 refer to refer 問題というのと、 ptr_fun っていうのが邪魔、って問題です。 まあ、実際にコードを見て頂くのが早いでしょう。

functionalTest.cc (実行はできません)

まず、refer to refer から。

class Class {
public:
    void func1(int i) {}
    void func2(Class& c) {}
};

こういうクラスがあって、

    std::vector<Class*> cs;
    Class c;

    // これは大丈夫です。
    std::for_each(cs.begin(), cs.end(),
                  std::bind2nd(std::mem_fun(&Class::func1), 1));
    // refer to refer!
#ifdef WATCH_ERROR
    std::for_each(cs.begin(), cs.end(),
                  std::bind2nd(std::mem_fun(&Class::func2), c));
#endif

    // これは当然大丈夫です。
    std::for_each(cs.begin(), cs.end(),
                  boost::bind2nd(boost::mem_fun(&Class::func1), 1));
    // refer to refer 関係無し。
    std::for_each(cs.begin(), cs.end(),
                  boost::bind2nd(boost::mem_fun(&Class::func2), c));

こうなっていて、ifdef で囲まれている所が STL の問題点です。 これは、func2 の引数が Class& で、 bind2nd でその型にもう一度 & を付けてしまっているため、 Class&& となってしまい、 これは標準 C++ では許されない型なので、 エラーになってしまいます。

これを防ぐため、boost ではテンプレート特別バージョンを使って、 Class& なら、Class&、Class なら Class& を返す、 不思議なメタ関数を用意しています。 それが、boost::call_traits というもので、 それを使って書き直されたものが用意されているのです。

まあ、慣れない内は std:: でエラーが出たら、 boost:: に変えればいいでしょう。 型の調整はコンパイル時に行われるので、 実行時間に影響は出ないので、 いっそデフォルトで boost:: でも良いかと思います。

次、ptr_fun の追い出し。

void func(int i, int j) {}

という関数があって、

    std::vector<int> is;

#ifdef WATCH_ERROR
    // これは、ダメで、
    std::for_each(is.begin(), is.end(), std::bind2nd(&func, 1));
#else
    // こうしなければならない。
    std::for_each(is.begin(), is.end(),
                  std::bind2nd(std::ptr_fun(&func), 1));
#endif

    std::for_each(is.begin(), is.end(), boost::bind2nd(&func, 1));

まあ、見ればわかりますね。 これも似たような原理で、 function object traits というもので 関数が渡されたか、関数オブジェクトが渡されたかを コンパイルタイムに判断しています。 解る人は以下を見れば意味が解るかと。 boost functional.hpp より。

    template <class Operation>
    struct unary_traits_imp;
     
    template <class Operation>
    struct unary_traits_imp<Operation*>
    {
        typedef Operation                         function_type;
        typedef const function_type &             param_type;
        typedef typename Operation::result_type   result_type;
        typedef typename Operation::argument_type argument_type;
    };
    template <class R, class A>
    struct unary_traits_imp<R(*)(A)>
    {
        typedef R (*function_type)(A);
        typedef R (*param_type)(A);
        typedef R result_type;
        typedef A argument_type;
    };

かっこいいなあ。


home / index

全てリンクフリーです。 コード片は自由に使用していただいて構いません。 その他のものはGPL扱いであればあらゆる使用に関して文句は言いません。 なにかあれば下記メールアドレスへ。

shinichiro.hamaji _at_ gmail.com / shinichiro.h