(( 注。 こういう感じで書いてある部分は私の注です。 これは翻訳ではなく、概略です。 正確に書くように努力しましたが、不正確ならすいません。 原文は http://www.mywikinet.com/mpl/ の三章です。 )) 3.1 使われる慣例 このチュートリアルではたくさんの省略した名称を使うよ。 std::vector じゃなくて vector とか。 mpl:: ってのは boost::mpl:: のこと。 mpl/alias.hpp で namespace mpl = boost::mpl がなされてますよ。 3.2 メタ関数 MPL ではよくクラステンプレート内で "type" に typedef するよ。 // on the face of it, not very useful template< typename T > struct identity { typedef T type; }; // perhaps more useful template< typename T > struct result_type { typedef typename T::result_type type; }; メタ関数の発動は、クラステンプレートのインスタンシエイトで行うのだよ。 テンプレートパラメータはメタ関数の「引数」。 さっきの typedef みたいな奴でとれる型がメタ関数の「戻り値」。 typedef identity::type t1; // t1 == int typedef result_type< std::unary_function >::type t2; // t2 == bool 3.3 コンパイル時 if 気合いの入ったメタプログラミングでは分岐が必要。 いくつかの条件分岐はテンプレート特別バージョンや、 関数オーバーロードで実現可能。 一般的には、なんか二つの型から一つの型を選択する基本ライブラリが欲しい。 boost::mpl では if_ というものを用意してる。 template< typename T > struct heap_holder { // ... private: boost::scoped_ptr m_object; }; template< typename T > struct stack_holder { // ... private: T m_object; }; template< typename T > struct can_be_on_stack : mpl::bool_c< (sizeof(T) <= sizeof(double)) > { }; // use 'if_' to choose where to store 'T' member template< typename T > struct lightweight : private mpl::if_< can_be_on_stack , stack_holder , heap_holder >::type { // ... }; (( 注。 64ビット越えるとヒープで持つべき、 ってのは Modern C++ Design にも書いてあります。 )) if_ の第一「引数」は Integral Constant Concept のモデルでないとだめ。 MPL はもうちょっと楽だけど一般的でないのも提供する。 (( 注。 Coding Guidelines for Integral Constant Expressions http://www.boost.org/more/int_const_guidelines.htm )) template< typename T > struct lightweight : private mpl::if_c< (sizeof(T) <= sizeof(double)) , stack_holder , heap_holder >::type { // ... }; 3-4. apply_if c++ の実行時では if-else の片方しか実行されないことが保証される。 void fun(giraffe* g) { if (g) cout << g->name(); else cout << "no giraffe"; } ただコンパイル時では上手くいかない。例えば以下のような。 template< typename T > struct pointed_type { typedef typename mpl::if_< boost::is_pointer , typename boost::remove_pointer::type , typename T::element_type // #1 >::type type; }; typedef pointed_type< std::auto_ptr >::type int_ptr; // ok typedef pointed_type::type char_ptr; // error in line #1! T == char* だと char*::element_type が不正なわけ。 if_ に渡すのは実際にコンパイル時実行された結果で無く、 if_ が結果を取得するため実行すべきメタ関数なのだ。 (( 注。 今の失敗例ってのは、実行時世界で書くと、 void fun(giraffe* g) { std::string s1(g->name()); std::string s2("no giraffe"); if (g) cout << s1; else cout << s2; } と書いているようなものだと理解すれば良いかと。 )) namespace aux { template< typename T > struct element_type { typedef typename T::element_type type; }; } を用意して、 template< typename T > struct pointed_type { private: // pick a metafunction typedef typename mpl::if_< boost::is_pointer , boost::remove_pointer , aux::element_type >::type func_; // #1 public: // apply the metafunction typedef typename func_::type type; }; これで OK。 これによって、boost::remove_pointer と aux::element_type は、 無条件にはインスタンシエイトされない。 こんなのテンプレートメタプログラミングでは当り前のこと。 (( 注。恐ろしい世界ですな。)) だから、再利用可能なコンポーネントを既に用意しとります。 それが apply_if。 template< typename T > struct pointed_type { typedef typename mpl::apply_if< boost::is_pointer , boost::remove_pointer , aux::element_type >::type type; }; 3.5 apply_if, part 2 以下のような pointed_type の汎用バージョン、 remove_pointer_if も両方実行してしまう問題をかかえている。 template< typename Condition , typename T > struct remove_pointer_if { typedef typename mpl::if_< Condition , typename boost::remove_pointer::type , T >::type type; }; そんな時は apply_if。 template< typename Condition , typename T > struct remove_pointer_if { typedef typename mpl::apply_if< Condition , boost::remove_pointer , mpl::identity >::type type; }; これで不必要な時には remove_pointer と identity は インスタンシエイトされない。