テンプレートの部分特殊化を利用した const 除去型 〜boost のソースを読む〜
過去記事「テンプレートの部分特殊化を利用した間接参照型推定」では,テンプレートのテンプレートの部分特殊化 (Wikipedia)を使ってポインタ型やイテレータ型からそれを間接参照した型(ポインタを取った型)を推定する方法を紹介した.この方法を使って,例えば和を求めるテンプレート関数の型引数をひとつ減らせられることを示した.しかしこの方法だけだと, const double 型の配列の要素を足し合わせようとしたときコンパイルエラーとなる.これを解決するための,ある型から const を除去した型を推定する方法を紹介する.ここで紹介するテクニックは,高度に抽象化されたテンプレート関数やテンプレートクラスを作るときに必要となる.ついでに, boost C++ ライブラリの remove_const の実装を読んでみよう.…正直,彼ら(boostの実装者たち)は変態さんだと思います.
「テンプレートの部分特殊化を利用した間接参照型推定」で作った和を計算するテンプレート関数は,こんな感じの実装だった:
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; }
例えば FwdItr が double* 型のとき, dereferenced_type
この sum だと, const double 型の配列の要素を足し合わせようとしたときコンパイルエラーが吐かれる.例えば,
const double x[]= {1.0,2.0,3.0,4.0,5.0}; cout<<"sum(x)= "<<sum(x,x+sizeof(x)/sizeof(x[0]))<<endl;
このコードはコンパイルエラー.なぜなら, sum の中で res は const double として宣言されてしまうため,値を代入できないからだ.
それならば.間接参照型を推定するのとまったく同じ手法で, const を除去した型を推定しようではないか.つまり,
template <typename T> struct elim_const_type { typedef T type; }; template <typename T> struct elim_const_type <const T> { typedef T type; };
こういう構造体を作るのだ. T が const 修飾されていない型なら elim_const_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 elim_const_type< typename dereferenced_type<FwdItr>::type >::type res(zero); if (first==last) return res; for (; first!=last; ++first) res += *first; return res; }
のように実装でき,上のコンパイルエラーを解決できる.
boost のソースを読む
さて. elim_const_type を実装した時点で本稿の目的は達せられたのだが,実は elim_const_type と同じ機能を持つテンプレート構造体が boost でも実装されているのだ.覗いてみよう.
// /usr/include/boost/type_traits/remove_const.hpp // (C) Copyright Dave Abrahams, Steve Cleary, Beman Dawes, Howard // Hinnant & John Maddock 2000. // Use, modification and distribution are subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt). // // See http://www.boost.org/libs/type_traits for most recent version including documentation. #ifndef BOOST_TT_REMOVE_CONST_HPP_INCLUDED #define BOOST_TT_REMOVE_CONST_HPP_INCLUDED #include <boost/type_traits/is_volatile.hpp> #include <boost/type_traits/broken_compiler_spec.hpp> #include <boost/type_traits/detail/cv_traits_impl.hpp> #include <boost/config.hpp> #include <boost/detail/workaround.hpp> #include <cstddef> #if BOOST_WORKAROUND(BOOST_MSVC,<=1300) #include <boost/type_traits/msvc/remove_const.hpp> #endif // should be the last #include #include <boost/type_traits/detail/type_trait_def.hpp> namespace boost { #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION namespace detail { template <typename T, bool is_vol> struct remove_const_helper { typedef T type; }; template <typename T> struct remove_const_helper<T, true> { typedef T volatile type; }; template <typename T> struct remove_const_impl { typedef typename remove_const_helper< typename cv_traits_imp<T*>::unqualified_type , ::boost::is_volatile<T>::value >::type type; }; } // namespace detail // * convert a type T to non-const type - remove_const<T> BOOST_TT_AUX_TYPE_TRAIT_DEF1(remove_const,T,typename boost::detail::remove_const_impl<T>::type) BOOST_TT_AUX_TYPE_TRAIT_PARTIAL_SPEC1_1(typename T,remove_const,T&,T&) #if !defined(BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS) BOOST_TT_AUX_TYPE_TRAIT_PARTIAL_SPEC1_2(typename T,std::size_t N,remove_const,T const[N],T type[N]) BOOST_TT_AUX_TYPE_TRAIT_PARTIAL_SPEC1_2(typename T,std::size_t N,remove_const,T const volatile[N],T volatile type[N]) #endif #elif !BOOST_WORKAROUND(BOOST_MSVC,<=1300) BOOST_TT_AUX_TYPE_TRAIT_DEF1(remove_const,T,typename boost::detail::remove_const_impl<T>::type) #endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION } // namespace boost #include <boost/type_traits/detail/type_trait_undef.hpp> #endif // BOOST_TT_REMOVE_CONST_HPP_INCLUDED
なんとなく BOOST_TT_AUX_TYPE_TRAIT_DEF1 とか BOOST_TT_AUX_TYPE_TRAIT_PARTIAL_SPEC1_1 あたりで remove_const を生成しているのはわかるが,詳細がわからない.そこで,
// remove_const.cpp #include <boost/type_traits/remove_const.hpp> int main(int argc, char**argv) { return 0; }
というコードを書いて,gcc で -E オプションをつけてコンパイルし, remove_const.ii に保存する.
% g++ -E remove_const.cpp > remove_const.ii
この -E オプションをつけると,コンパイルはせずにプリプロセス処理が終了したところで停止する.つまり,include がすべて読み込まれ,マクロがすべて展開されたコードが出力されるというわけだ.上の boost みたいな,マクロだらけのコードを読むときに使える.
さて,生成された remove_const.ii は余分なコードも含んでいて読みにくいので, remove_const に関係する部分だけ抜きだしてみた:
namespace boost { namespace detail { template <typename T> struct cv_traits_imp {}; template <typename T> struct cv_traits_imp<T*> { static const bool is_const = false; static const bool is_volatile = false; typedef T unqualified_type; }; template <typename T> struct cv_traits_imp<const T*> { static const bool is_const = true; static const bool is_volatile = false; typedef T unqualified_type; }; template <typename T> struct cv_traits_imp<volatile T*> { static const bool is_const = false; static const bool is_volatile = true; typedef T unqualified_type; }; template <typename T> struct cv_traits_imp<const volatile T*> { static const bool is_const = true; static const bool is_volatile = true; typedef T unqualified_type; }; } // end of namespace detail } // end of namespace boost //------------------------------------------------------------------------------------------- namespace mpl_ { struct integral_c_tag { static const int value = 0; }; } namespace boost { namespace mpl { using ::mpl_::integral_c_tag; } } //------------------------------------------------------------------------------------------- namespace mpl_ { template< bool C_ > struct bool_; typedef bool_<true> true_; typedef bool_<false> false_; } // end of namespace mpl_ namespace boost { namespace mpl { using ::mpl_::bool_; } } namespace boost { namespace mpl { using ::mpl_::true_; } } namespace boost { namespace mpl { using ::mpl_::false_; } } namespace mpl_ { template< bool C_ > struct bool_ { static const bool value = C_; typedef integral_c_tag tag; typedef bool_ type; typedef bool value_type; operator bool() const { return this->value; } }; template< bool C_ > bool const bool_<C_>::value; } // end of namespace mpl_ namespace boost{ template <class T, T val> struct integral_constant /*: public mpl::integral_c<T, val>*/ { typedef integral_constant<T,val> type; }; template<> struct integral_constant<bool,true> : public mpl::true_ { typedef integral_constant<bool,true> type; }; template<> struct integral_constant<bool,false> : public mpl::false_ { typedef integral_constant<bool,false> type; }; typedef integral_constant<bool,true> true_type; typedef integral_constant<bool,false> false_type; } // end of namespace boost //------------------------------------------------------------------------------------------- namespace boost { template< typename T > struct is_volatile : ::boost::integral_constant<bool,::boost::detail::cv_traits_imp<T*>::is_volatile> { }; template< typename T > struct is_volatile< T& > : ::boost::integral_constant<bool,false> { }; } // end of namespace boost //------------------------------------------------------------------------------------------- namespace boost { namespace detail { template <typename T, bool is_vol> struct remove_const_helper { typedef T type; }; template <typename T> struct remove_const_helper<T, true> { typedef T volatile type; }; template <typename T> struct remove_const_impl { typedef typename remove_const_helper< typename cv_traits_imp<T*>::unqualified_type , ::boost::is_volatile<T>::value >::type type; }; } // end of namespace detail template< typename T > struct remove_const { typedef typename boost::detail::remove_const_impl<T>::type type; }; template< typename T > struct remove_const<T&> { typedef T& type; }; template< typename T, std::size_t N > struct remove_const<T const[N]> { typedef T type[N]; }; template< typename T, std::size_t N > struct remove_const<T const volatile[N]> { typedef T volatile type[N]; }; } // end of namespace boost // 一部コメントアウトしてある.このコードだけでも elim_const_type の代わりに使用できる. //-------------------------------------------------------------------------------------------
ふーむ.なんとなく読める. cv_traits_imp で const/volatile の除去型 (unqualified_type) を定義しているようだ.本稿の elim_const_type と違ってポインタに変換してから const/volatile を除去している.配列にも対応しているようだし,いろんな状況に対応しているようだ.が.全体像が掴めない.ふーむ.そろそろ潮時のようですな.orz...