静的次元解析
Bjarne Stroustrupさんが、C++11仕様の決定を記念して書いたC++のコーディング・スタイルに関する記事 Software Development for Infrastructureで紹介されていた静的次元解析のコードを試しに実装してみた。
C++11スタイルでコードを書くと今までのスタイルとまったく違うのコードになるのに驚いた。しかし、 C++のテンプレート静的に(実行時のオーバーヘッドなしで)、ここまでできるとは素晴らしい。
上記記事、ここに挙げた静的次元解析以外にもC++で実装するときにスタイルについて、助言が書かれているので お勧めです。
1: #include <iostream> 2: 3: // 単位を表現するテンプレート・クラス。 4: template <int M, int K, int S> 5: struct Unit { 6: enum {m = M, kg = K, sec =S}; 7: }; 8: // 単位付き値を表現するテンプレート・クラス。 9: template <typename Unit> 10: struct Value { 11: double val; 12: explicit constexpr Value(double b) : val(b) {} 13: }; 14: 15: using Speed = Unit<1,0,-1>; 16: using Length = Unit<1,0,0>; 17: using Time = Unit<0,0,1>; 18: 19: // -------------------------ユーザ定義リテラルの定義----------------------------------------- 20: /* 21: C++には組み込みのリテラルがありました。C++11から、ユーザがリテラルを定義できるようになりました。 22: 23: true; boolリテラル。 24: "hello, world!"; 文字列リテラル。 25: 1l; longリテラル。 26: 1.0d; doubleリテラル。 27: 28: 型安全なコードを書くために以下のようなリテラルを定義します。リテラルには(_)アンダースコアが 29: 含まれていることに注意。(_)を付けないリテラルは標準で予約さているので警告。 30: 31: 30.0_km; 30 kmを表現するリテラル。 32: 2.0_hr; 2時間を表現するリテラル。 33: */ 34: constexpr Value<Length> 35: operator"" _km(long double d) { 36: return Value<Length>(1000*d);// Lengthの基本単位がメートルなので、1000倍して、kmからmに変換。 37: } 38: 39: constexpr Value<Time> 40: operator"" _hr(long double d) { 41: return Value<Time>(60*60*d);// Timeの基本単位が秒(sec)なので、時から秒に変換。 42: } 43: 44: // ------------------------ 単位付き値に対する演算を定義。------------------------------------- 45: template <typename Unit> 46: Value<Unit> operator+(Value<Unit>&& lhs, Value<Unit>&& rhs) { 47: return Value<Unit>(lhs.val + rhs.val); 48: } 49: 50: template <typename Unit1, typename Unit2> 51: Value<Unit<Unit1::m - Unit2::m, Unit1::kg - Unit2::kg, Unit1::sec - Unit2::sec>> 52: operator/(Value<Unit1>& n, Value<Unit2>& d) { 53: return Value<Unit<Unit1::m - Unit2::m, Unit1::kg - Unit2::kg, Unit1::sec - Unit2::sec>>(n.val/d.val); 54: } 55: 56: // Valueが正しい単位を持っているか確認するための関数テンプレートisSpeed。 57: template <typename T> 58: constexpr bool isSpeed(){ return false; } 59: 60: template <> 61: constexpr bool isSpeed<Value<Speed>>(){ return true; } 62: 63: // Valueを表示するために<<演算子をオーバーロード。 64: template <typename Unit> 65: std::ostream& operator<<(std::ostream& os, Value<Unit>& v){ 66: return (os << v.val << std::endl); 67: } 68: 69: int main() { 70: // 家からオフィスまでの通勤時の平均時速を求めたい。 71: auto homeToOffice(30.0_km);// 家からオフィスまでの距離。30km 72: auto time(1.0_hr);// 所要時間 1.0時間 73: auto average_speed = homeToOffice/time;// 時速を計算。 74: // 計算したaverage_speedの単位が正しいかチェック。 75: static_assert(isSpeed<decltype(average_speed)>(),"Check failed!!"); 76: std::cout << average_speed << std::endl; 77: 78: // 距離と時間は足せない。コンパイル・エラー。 79: // auto bug_1 = homeToOffice + time; 80: 81: // 時速を計算したいのに割り算が逆。static_assert部分でコンパイル・エラー。 82: // auto bug_2 = time/homeToOffice; 83: // static_assert(isSpeed<decltype(bug_2)>(),"Check failed!!"); 84: 85: return 0; 86: }