右演算専用データの実装
次のような線形演算子を考えてみる。
O * K → K
O * O → O
K * K → 未定義
K * O → 未定義
これより以下のようなことも成り立つ。
O * O ・・O * K → K
O * O ・・O * O → O
O * (O * O) → O
O * (O * K) → K
これを実装使用とするとまずは以下のようになると思われる。
class K{ //特に定義する演算子はない }; class O{ public: K operator*( const K& k )const;//O*Kの定義 O operator*( const O& o )const;//O*Oの定義 };
しかし、ある事情があってO*Oのデータの生成が困難である場合で、以下のような制限をうけた場合はどうすればよいか?
O * O → 単体では生成させない
O * O ・・O * O * K → O * (O * (O * ・・・O * (O * K))・・・)
つまり、すべて右演算だけで実現する。左演算を右演算に振りかえるようにするにはどうすればいいか?という問題である。もちろん実際使う際に右演算になるようにすべてカッコでくくれば出来るわけだが、そんな手間をかけないでカッコでくくらなくても右演算子に変換されるようにしたい。
解決方法として思いついたのが以下のようなExpressionTemplateもどきである。
class K{ //やはり特に定義する演算子はない }; class O{ public: K operator*( const K& k )const;//O*Kの定義 O_< O > operator*( const O& o )const{ return O_< O >( *this, o );//中間状態を返す。 } }; //中間状態の定義 template< class L > class O_{ public: O_( const L& l, const O& o ):m_l(l),m_o(o){} O_< O_< L > > operator*( const O& o )const{ return O_< O_< L > >( *this, o );//中間状態を返す。 } //Kとの演算を返す。 K operator*( const K& k )const{ return m_l * (m_o * k); } private: const L m_l; const O m_o; };
O_という中間状態を定義して一時的に計算を保留させておくわけである。そして最後に一気に計算するわけである。
実際、どのように実現されているか実験してみる。
仮に以下のコードがすべてインライン展開されたと仮定する(メタメタしてないので多分されない)。
K k1; O o1, o2, o3, o4, o5; K k2 = o1 * o2 * o3 * o4 * o5 * k1;
まずはo1*o2が展開される
K k2 = O_< O >( o1, o2 ) * o3 * o4 * o5 * k1;
次にo3が展開される。
K k2 = O_< O_< O > >( O_< O >( o1, o2 ), o3 )* o4 * o5 * k1;
最後まで展開される。
K k2 = O_< O_< O_< O_< O > > > >( O_< O_< O_< O > > >( O_< O_< O > >( O_< O >( o1, o2 ), o3 ), o4), o5) * k1;
ここで、O_< O_< O_< O_< O > > > >の一時オブジェクトとk1の演算が展開される
K k2 = O_< O_< O_< O > > >( O_< O_< O > >( O_< O >( o1, o2 ), o3 ), o4) * (o5 * k1);
さらに展開される
K k2 = O_< O_< O > >( O_< O >( o1, o2 ), o3 ) * (o4 * (o5 * k1));
最後まで展開すると・・・
K k2 = o1 * (o2 * (o3 * (o4 * (o5 * k1))));
というわけでめでたく右演算のみで表される。
確実に展開させたければTemplateMetaprogramingでもっと頑張ればいいのだが疲れたのでこれまでorz