異なる意味の値同士の演算の防止
2016-02-01 | [Programming]C++ の typedef は,単に既存の型の別名を定義するだけで,新たな型を定義したりはしない. たいていの場合は,「元の型とまったく同様に使える」のは有用である. 例えば STL のコンテナの iterator は長ったらしい型になるので,それに短い別名をつけるのは合理的である.
しかし,値の意味が違うことを表すために型名を変えたいこともある.そのような場合には,違う意味の値同士でむやみに演算ができないようになっていたほうが便利である. 例えば「数独のグリッド上の Y 座標」と「数独で,グリッドのセルに書いてある数」は意味がまるで違い,この 2 つを足したりしていたら恐らくプログラムのミスである. また,この 2 種類の値を受け取る関数があったとして,引数を逆に指定していたりしてもプログラムのミスであろう. そのような場合にはコンパイルエラーになるようにするとバグを予防するのに役立ちそうである.
そのような目的のためには,単に struct で値をラップしてやればよい. しかし,それだけだと同じ型同士の演算もまどろっこしいため,それなりに便利に使えそうな型安全 typedef を作った.
実装
ここ にある. パズルソルバープロジェクトで使うため namespace がついているが,これは適当に変更すればよい.
少なくとも整数型については正しく動作するはずである.それ以外の型については試していないのでわからない. ただ,複雑な型についてこれを使うとオブジェクトのコピーが発生して面倒な気はする.
仕掛けは単純で,異なる typed_number 型同士の演算を明示的に禁止しているだけである. (delete 宣言を行わないと,typed_number<I, T> から I への変換関数が呼び出されることにより,演算ができてしまう)
例
仕様
- 任意の型について,それをラップして「プログラム上の意味」の異なる「ほぼ同じ挙動の」型を作ることができる.
- typedef int hoge; と書くかわりに,STRICT_TYPEDEF(int, hoge); と書く.
- 新たに作った型に対しても,元の型とほぼ同じように演算子が使える.演算子のオペランドには,同じ型の値以外に,元の型の値も用いることができる.
- これは,定数を足すなどの処理を簡潔に記述するためである.
- なので,そんなに strict ではない.
- STRICT_TYPEDEF で作った異なる型同士は,そのままでは演算ができない.
- STRICT_TYPEDEF で作った型のコンストラクタは explicit 指定がされているため,元の型から勝手に新たな型への変換が起きることはない.
- hoge a = 5; のような書き方もできない.
- 逆に,新たな型から元の型への変換は勝手に行われる.
- これは,配列の添字としても簡単に使えるようにするためである.
これらの仕様は,ソースを適当に改変すればかなり好き勝手に変更することができる.