2017-10-12 16 views
1

私は、関数のパラメータを強く入力できるように、ユニットを表現するためのユーザー定義型を作成しようとしています。例えば、長さはミリメートル、速度はミリメートル/秒、加速のためには毎秒ミリメートルなどがあります。私はこれまでのところ、これを行っているユーザー定義型 - 単位、変換

mm dist = 5.5_mm; 
mm dist1 = dist + 10_mm; 
mmpersec velocity = mmpersec(50.0); 
mmpersec2 accel = 100.0_mms2; 

void set_accel(mmpersec2 accel) { _accel = accel; } 

:私は次のように使用できるユニットを作成する

using meters = Value<double, mksUnit<1, 0, 0>>; 
using mm = Value<double, mksUnit<-3, 0, 0>>; 
constexpr mm operator""_mm(long double v) { return mm(static_cast<double>(v)); } 
using mmpersec = Value<double, mksUnit<-3, 0, 1>>; 
constexpr mmpersec operator""_mms(long double v) { return mmpersec(static_cast<double>(v)); } 
using mmpersec2 = Value<double, mksUnit<-3, 0, 2>>; 
constexpr mmpersec2 operator""_mms2(long double v) { return mmpersec2(static_cast<double>(v)); } 

:これにより

template<typename T, typename mksUnit> 
struct Value 
{ 
    // data member 
    T val; 

    // constructor 
    constexpr explicit Value(T d) : val(d) {} 

    // operator overloads 
    Value & operator +=(const Value &rhs) { val += rhs.val; return *this; } 
    // other math and compare 
    // operators, etc. 
} 

template<int meters, int kilo, int seconds> 
struct mksUnit 
{ 
    enum { m = meters, k = kilo, s = seconds }; 
}; 

を、私のようなことを行うことができます

私は彼はいくつかのタイプの間で変換を行う必要があり、私はそれを行う良い方法を探しています。私が持っている唯一のアイデアは、クラスから継承し、型オーバーロードを派生クラスに追加することです。

struct millimeters: public mm 
{ 
    operator(meters) { return mm.val/1000; } 
} 

などです。例えば、1.025にLEN設定する必要があり

meters len = meters(5.0); 
len += millimeters(25); 

は、私のような事をやりたいです。

私の質問は、上記のように派生したさまざまなユーザー定義型間でコンバージョンを追加する最もよい方法です。

+2

これは本当に気まぐれに奇妙です。なぜここでテンポを使うのですか?あなたは '' mm''の定義がコンパイル時に変動する可能性のある宇宙に住んでいますか?これらのことはあなたが得ることになる定数に近いものです。 – tadman

+1

私はあなたがこれに間違ったやり方で取り組んでいると感じています。おそらく、時間、距離、重量または質量、温度などの基本単位の単一のタイプが必要です。コンパイル時に質量と時間を必要とする関数を調べることができます。あなたがしなければならない変換は、すべての値を何らかの内部標準(mm、秒、°Kなど)に正規化することで内部的に行うことができます。 – tadman

+0

このアプローチは 'unit :: energy momentum(const unit :: mass&m、const unit :: veolocity&v)'のようなコードにつながります。ここで 'unit :: velocity(const unit :: distance&d、const unit: :time&t) 'を生成します。あなたは* mph *や* inch-pounds *のような便利なコンストラクタや、あなたの問題に実際に適用しているユニットを用意することができます。 – tadman

答えて

1

あなたは間違った方向からあなたの問題に取り組んでいると思います。任意の単位を変換するのではなく、ある量の単位を変換することを望みます。

だから私はこのように起動します:

struct Quantity { 
    double value; 
}; 

// Base Quantites 
class Length : public Quantity {}; 
class Duration : public Quantity {}; 

// Base units 
Length Meter() { 
    Length l; 
    l.value = 1; 
    return l; 
} 

Length Millimeter() { 
    Length l; 
    l.value = 0.001; 
    return l; 
} 

Duration Seconds() { 
    Duration t; 
    t.value = 1; 
    return t; 
} 

は、今、私たちは私たちの量は、我々は、変換を開始することができ、整理しました:

// It is a good Idea to use SFINAE here to enable this function 
// only for derived classes of Quantity 
template <class Quantity> 
double transform(Quantity&& value, Quantity&& ratio) { 
    return value.value/ratio.value; 
} 

派生ユニットの一部は、トリッキーなものです:

template <class Q1, class Q2> 
struct MultipliedQuantiy : public Quantity {}; 

// I would use SFINAE here too because it is a bad idea to 
// define a multiplication for all possible types. 
template <class Q1, class Q2> 
auto operator*(Q1 q1, Q2 q2) { 
    MultipliedQuantiy<Q1, Q2> q; 
    q.value = q1.value * q2.value; 
    return q; 
} 

例として、次のように使用できます。

auto area = 1000.0*Millimeter()*(1000.0*Millimeter()); 
auto squareMeter = 1.0*Meter()*Meter(); 
double area_in_m2 = transform(area, squareMeter); 

これはどのように行うことができるのかという基本的な考えです。残りの作業を実現することはあなた次第です。コンパイル時間の評価を強制するために、多くの場所でconstexprを使用することもできます。

+0

練習のポイントは、 '通常の'データ型のように使うことができるが、暗黙的な変換を許可しない単位を作成することです。あなたの '変換'のような余分な関数を使うことは、構文を追加せずに変換する方法を知っている型の自然な使用を取り除きます。私が上で説明し実装したデータ型は非常に自然に動作し、誤ってメートルをミリ、またはラジアンでミックスすることは不可能です。私はちょうどあなたの変換関数のようなものを使用せずに数学と割り当てを行うことができるように慈悲を行うエレガントな方法を考え出したいと思います。 – ttemple

+0

なぜ暗黙の変換を不可能にしたいのですか?あなたの身体的背景は、あなたがmmメーターを入力しても同じです、両方とも長さでスカラー量です。だから、なぜ誰かがメートルとミリで操作をしないようにしたいのですか? – OutOfBound

+0

mmをメートルに追加することは、mmがメートルに変換する方法を知っている限り、問題ありません。私がmm = double、meters = double、mmをメートルに追加すると、私は悪い答えがあります。 mm = double、mm = intの場合、暗黙的な変換のために追加が可能ですが、その答えは私の望むものではありません。私は、データユニットがどこかに暗黙的に変換されるのではなく、直接的に互換性があることをインターフェイスが知っていることを確認したい。 – ttemple

関連する問題