テンプレートの部分特殊化を利用した間接参照型推定
C++の機能,テンプレートの部分特殊化 (Wikipedia)を使って,あるポインタ型orイテレータ型から,それを間接参照した(or逆参照した,orポインタをとっぱらった)型を推定する方法を紹介する.
例えば,過去の記事d:id:aki-yam:20081024:1224846960で,和を求めるテンプレート関数
template <typename T, typename ForwardIterator> inline T sum (ForwardIterator first, ForwardIterator last, const T &zero=0.0l) { T res(zero); if (first==last) return res; for (; first!=last; ++first) res += *first; return res; }
を紹介した.この関数は少なくとも STL コンテナ,配列,liboctave の ColumnVector に対して共通に使用することができた.
さて,このテンプレートで, "typename T" ははたして必要なのだろうか? つまり, ForwardIterator first として宣言されている first (これはイテレータであったりポインタであったりする)を間接参照した型が T でなければならないから, *first の型が分かれば T を明示する必要が無くなるはずなのだ.しかし,現行の C++ にはインスタンスの型を取得する方法は無い*1. C++0x では可能になるようだが(C++0x インスタンスからメンバの型を取得するあたりを参照),それは未来の話.
そこで,応急処置的な代用方法として,上の ForwardIterator 型を間接参照した型を取得する方法を考えてみた.一番シンプルに実現できるのが,おそらくテンプレートの部分特殊化 (Wikipedia)を使った方法で,具体的にはこうする:
template <typename T> struct dereferenced_type { typedef typename T::value_type type; }; template <typename T> struct dereferenced_type<T*> { typedef T type; }; template <typename FwdItr> inline typename dereferenced_type<FwdItr>::type sum (FwdItr first, FwdItr last, const typename dereferenced_type<FwdItr>::type &zero=0.0l) { typename dereferenced_type<FwdItr>::type res(zero); if (first==last) return res; for (; first!=last; ++first) res += *first; return res; }
ひとつ目のテンプレートクラス dereferenced_type は, T がイテレータクラスであることを仮定している.すべてのイテレータクラスでは, value_type 型が内部で定義されているから, T::value_type はイテレータを間接参照した型を表す.よって, dereferenced_type
ただし,これだけだと T=int* のような場合に対処できない.そこで,ふたつ目の dereferenced_type によって,もとの dereferenced_type の部分特殊化を行っている.これは型 T のポインタ型に対する特殊化である.この場合, T* を逆参照した型は当然 T であるから, dereferenced_type
最後のテンプレート関数がこれを利用した sum の定義だ. dereferenced_type
ちなみに,上の sum を利用するには,d:id:aki-yam:20081024:1224846960にあるサンプルから T を指定する部分,すなわち
cout<<"sum(x)= "<<sum(x.fortran_vec(),x.fortran_vec()+x.length())<<endl; cout<<"sum(y)= "<<sum(y.begin(),y.end())<<endl; cout<<"sum(z)= "<<sum(z,z+sizeof(z)/sizeof(z[0]))<<endl;
のようにすれば利用できる.
余談だが, boost にもポインタを取り去る remove_pointer というテンプレートクラスが存在する. boost/type_traits/remove_pointer.hpp か boost/type_traits.hpp を include すれば使える.が,上記 dereferenced_type の代わりに使ってもうまく機能しなかった.イテレータを間接参照した型を取得するようにはできていなかったためだ.