2011-05-31 36 views
1

當閱讀<ratio><chrono>時,我試圖想象一個Length類型,它可以防止意外轉換錯誤。C++ 0x使用<ratio>更安全長度類型

這是我得到:

#include <iostream> 
#include <ratio> 
using namespace std; 

template<typename Scale> 
struct Length { 
    long long val_; 
    Length(long long val) : val_{val} {} 
    Length() = default; 
    Length(const Length&) = default; 
    Length& operator=(const Length&) = default; 
    // conversion 
    template<typename Scale2> 
    Length(const Length<Scale2> &other) 
    : val_{ other.val_*(Scale2::num*Scale::den)/(Scale2::den*Scale::num) } 
    { } 
    // access 
    long long value() const { return val_; } 
}; 
typedef Length<ratio<1>> m; 
typedef Length<kilo> km; 
typedef Length<milli> mm; 
typedef Length<ratio<1000,1094>> yard; 

要這樣使用

int main() { 
    km len_km = 300; 
    mm len_mm = len_km; 
    cout << " millimeter:" << len_mm.value() << endl; 
    cout << " m:" << m{len_km}.value() << endl; 
    cout << " yd:" << yard{len_km}.value() << endl; 
} 

現在我可以添加所有的+*操作獲得真正舒適... :-)

我想知道:

  • 是否有一個更容易訪問算術設施durationtime_point定義在<chrono>無論如何?我可以使用這些減少Length的努力嗎?
  • 編譯時常量(Scale2::num*Scale::den)/(Scale2::den*Scale::num)轉換構造函數(fraction/underflow?)中似乎很危險,但我無法找到更好的元編程方式,這裏的任何提示?

回答

2
  • 是否有可以 算術設施是 durationtime_point定義 <chrono>反正一個更容易獲得?我可以使用那些 減少Length的努力嗎?

對於「混合模式」算術和比較,您可以採取的common_type<T1, T2>::type優勢定義返回類型。 duration專業common_type是Period1和Period2的最大公約數,其中Period1和Period2是參與算術或比較操作的兩個ratio。你可以使用它像:

template <typename Scale1, typename Scale2> 
    typename std::common_type<Length<Scale1>, Length<Scale2>>::type 
    operator+(Length<Scale1> x, Length<Scale2> y); 

不幸的是,你必須重塑如何在編譯時得到兩個ratio S上的最大公約數。開始於編譯時gcd和lcm元函數的無符號long long。

嗯...或者你也許可以將你的專業化common_type基於已經完成的duration。您可以將結果durationperiod重新解釋爲您的Length的比例因子。我沒有原型,只是一個想法。

  • 的編譯時間常數 (Scale2::num*Scale::den)/(Scale2::den*Scale::num)轉換 構造似乎危險(分/下溢?), 但我想不出更好的 元編程的方式,在這裏任何提示?

同意。 duration處理這個問題有:

template <class Rep2, class Period2> 
    constexpr duration(const duration<Rep2, Period2>& d); 

備註:此構造不得 參與重載決議 除非treat_as_floating_point<rep>::value是真實的或兩者 ratio_divide<Period2, period>::den是是假的。 [注意:此需求在 在基於積分的 持續時間類型之間轉換時可防止 隱式截斷錯誤。這樣的構造很容易導致關於持續時間的值的混淆。 - 尾註]

I.e.你需要enable_if你的Length轉換構造函數,這樣它才存在,如果轉換是準確的(如果你想根據你的整數類型的長度)。對於準確的轉換,轉換因子(Scale2::num*Scale::den)/(Scale2::den*Scale::num)必須是可計算的,不能除法(除1除外)。您可以使用ratio_divide爲您執行此除法,然後得到的分母必須爲1(用於精確轉換)。

enable_if<ratio_divide<Scale2, Scale1>::type::den == 1, ...> 

這是一個很好的學習項目ratio!玩的開心! :-)

+0

我仍然在我有趣的項目列表中。你在這裏給了我一個很好的支持。我可以在大多數地方跟蹤你,我同意需要* gcd *。編譯時* gcd *對我來說已經是一個相當大的挑戰了,我猜(constexpr不能遞歸,對嗎?單線非遞歸循環gcd計算在哪裏?嘆*)。我還沒有找到時間。但我會......我會的。 – towi 2011-10-10 19:51:02

+0

這個代碼是免費使用,但你想要的只是保留它的版權:http://llvm.org/svn/llvm-project/libcxx/trunk/include/ratio – 2011-10-10 20:56:50

1

您使用整數類型來表示物理量。這不是人們通常想要的。如果你堅持一個整數類型,至少要按照正確的順序進行乘法和除法,即首先進行所有乘法運算,然後再進行除法運算(比較100 *(255/256)和(100 * 255)/ 256)。

在相關說明中,請記住,您將長度乘以長度,您將得到一個區域,而不是長度。考慮到這一點的實際圖書館存在,例如見siunits

+0

好點,是的。我的破壞意圖是在運行時只需要一次乘法就可以獲得一個編譯時間常量。但是你是對的,這裏用'int'就是壞的。但現在我將以此爲例。而且,我聽說過SIUnits-lib,並從那裏獲得了靈感。但我只想解釋新的C++ 0x-Stdlib - 我試圖找到一個使用''而沒有''的例子。你也是對的,我可能會忘記只用標量符號來操作operator *()。唉,* siunit *沒有成爲新的標準。真可惜,不是嗎? – towi 2011-05-31 10:25:44

+1

當100,255和256是編譯時常量,並且想要最小化溢出的可能性時,首先要計算出分子和分母的公因子。例如gcd(100,256)是4.所以你可以將100和256除以4.現在你的問題是(25 * 255)/ 64。這個問題不能進一步減少,所以最終的答案是(以合理的形式)6375/64。這是'ratio_divide'爲你所做的,並且在編譯時都是這樣。此外,如果溢出是不可避免的(即使是gcd的因素),你會發現在編譯時,而不是運行時。 – 2011-05-31 16:46:53