仕事場の机に「空の境界 上・下」(asin:4061823612, asin:4061823620)を置かれてしまったんですが,これは要するに「読め」ということですか?そうですか・・・.
Chained Inheritance(鎖状継承とでも訳すべきですかね?)
以下のようなクラス群を考えます.
class nil_t{}; templateclass A : public Super { }; template class B : public Super { }; template class C : public Super { };
Aのテンプレート引数T1とT2,Bのテンプレート引数T,Cのテンプレート引数T1とT2は各々のクラスに必要なテンプレート引数だと思ってください.上のA,B,Cに共通する事としてテンプレート引数で自身の親クラスを指定できるということが挙げられます.また,そのデフォルトはnil_tという空のクラスに指定されています.
さて,クラスDをクラスCから継承してみます.
class D : public C{ };
何の問題も無いですね?一つだけ,Cが暗黙のうちにnil_tから継承されているということには注意が必要です.
さて,では以下のように書くとどうなるでしょうか?
class D : public C> { };
まず,B
となれば,後は以下のように自由に継承の鎖を生成できるということは簡単に想像できるかと思います.
class D : public B> > { };
上の例ではnil_t → C
このChained Inheritanceを利用する目的とその実例として,自分が知っている範囲内では
- (インターフェースやポリシーを規定するような)空クラスを多数継承する際に,EBCOが効かない環境での空間のオーバーヘッドの削減を図る -> boost::operators
- 継承によってクラスの構造(データメンバ)の構築を行う -> boost::property
あたりが挙げられるでしょうか?珍しいテクニックではあると思います.
浮動小数点演算回数計測クラス
浮動小数点演算(FLoating point Operation)の回数をカウントする以下のような非常に単純なクラスを作ってみました.
#include#include template< class Real = double, class Counter = long long> class flo_counter : public boost::unit_steppable , boost::euclidian_ring_operators , boost::euclidian_ring_operators , Real, boost::partially_ordered , boost::partially_ordered , Real > > > > > { public: flo_counter(Real const &r = Real()) : val(r) { } flo_counter(flo_counter const &r) : val(r.val) { } flo_counter &operator++() { return ++val; } flo_counter &operator--() { return --val; } #define DEFINE_ASSIGNMENT_OPERATOR(OP) \ flo_counter & operator ## OP(flo_counter const &rhs) \ { \ val OP rhs.val; \ ++cnt; \ return *this; \ } \ #define DEFINE_ASSIGNMENT_OPERATOR2(OP) \ flo_counter & operator ## OP(Real const &rhs) \ { \ val OP rhs; \ ++cnt; \ return *this; \ } \ DEFINE_ASSIGNMENT_OPERATOR(+=) DEFINE_ASSIGNMENT_OPERATOR(-=) DEFINE_ASSIGNMENT_OPERATOR(*=) DEFINE_ASSIGNMENT_OPERATOR(/=) DEFINE_ASSIGNMENT_OPERATOR(%=) DEFINE_ASSIGNMENT_OPERATOR2(+=) DEFINE_ASSIGNMENT_OPERATOR2(-=) DEFINE_ASSIGNMENT_OPERATOR2(*=) DEFINE_ASSIGNMENT_OPERATOR2(/=) DEFINE_ASSIGNMENT_OPERATOR2(%=) #undef DEFINE_ASSIGNMENT_OPERATOR #undef DEFINE_ASSIGNMENT_OPERATOR2 #define DEFINE_BOOLEAN_OPERATOR(OP) \ bool operator ## OP(flo_counter const &rhs) const \ { \ return val OP rhs.val; \ } \ #define DEFINE_BOOLEAN_OPERATOR2(OP) \ bool operator ## OP(Real const &rhs) const \ { \ return val OP rhs; \ } \ DEFINE_BOOLEAN_OPERATOR(<) DEFINE_BOOLEAN_OPERATOR(==) DEFINE_BOOLEAN_OPERATOR2(<) DEFINE_BOOLEAN_OPERATOR2(>) DEFINE_BOOLEAN_OPERATOR2(==) #undef DEFINE_BOOLEAN_OPERATOR #undef DEFINE_BOOLEAN_OPERATOR2 flo_counter sqrt() const { return flo_counter(std::sqrt(val)); } flo_counter abs() const { return flo_counter(std::abs(val)); } static Counter &count() { return cnt; } private: Real val; static Counter cnt; }; template Counter flo_counter ::cnt = 0;
sqrtやabsのようなあまり本質的でないメンバ関数が入ってますがあまり気にしないでください.このクラスで一番目立つのは継承の部分でしょうか?Boost.Operatorsの枠組みに従うと必然的にこうなります.この継承の部分はよく見れば分かりますが,CRTPとChained Inheritanceの複合技です.Boost.OperatorsがCRTPで何をしているかについてはid:Cryolite:20040605#p2あたりを参考にしてみてください.
上のflo_counterはこれで組み込みの浮動小数点型とほとんど同等なものとして振舞うことが可能になります.でもこれだけでは全然面白くないので,次回はこれをboost::numeric::ublas::vectorあたりにでも突っ込んでみようかと思います.