詳しくはC++11中ratioコンパイル期間点数(一)

25057 ワード

スコアについては、C++の標準ライブラリにはこのようなクラスは提供されておらず、必要に応じて完全に自分で実現することができるが、C++11はstd::ratioヘッダファイルに定義されたコンパイル期間定数スコアクラスを提供する.
このクラスでは、コンパイル期間のスコアを具体的に指定し、コンパイル期間の演算を実行できます(最も簡略化できます).また、コンパイル期間は安全です.これはソースコードで見ることができます.分母が0のような異常はコンパイル期間で取得できます.
まず、このstd::ratioの使い方を見てみましょう.
template<long long N, long long D>
std::ostream & operator << (std::ostream & os, const std::ratio<N, D> && r)
{
    return os << r.num << " / " << r.den << std::endl;
}

int main()
{
    std::cout << std::ratio<5, 3>();
    std::cout << std::ratio<2, 2>();
    std::cout << std::ratio<0, 3>();
    std::cout << std::ratio<4, 6>();
    std::cout << std::ratio<-4, 6>();
    std::cout << std::ratio<4, -6>();
    std::cout << std::ratio<-4, -6>();
    return 0;
}

このDemoはstd::ratioの特性を詳細に示すことができる.
では、ratioはどのように実現されているのか、まずソースコードを見てみましょう.
/**
 *  @brief Provides compile-time rational arithmetic.
 *
 *  This class template represents any finite rational number with a
 *  numerator and denominator representable by compile-time constants of
 *  type intmax_t. The ratio is simplified when instantiated.
 *
 *  For example:
 *  @code
 *    std::ratio<7,-21>::num == -1;
 *    std::ratio<7,-21>::den == 3;
 *  @endcode
 *
*/
template<intmax_t _Num, intmax_t _Den = 1>
struct ratio
{
    static_assert(_Den != 0, "denominator cannot be zero");
    static_assert(_Num >= -__INTMAX_MAX__ && _Den >= -__INTMAX_MAX__,
                  "out of range");

    // Note: sign(N) * abs(N) == N
    static constexpr intmax_t num =
        _Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value;

    static constexpr intmax_t den =
        __static_abs<_Den>::value / __static_gcd<_Num, _Den>::value;

    typedef ratio<num, den> type;
};

template<intmax_t _Num, intmax_t _Den>
constexpr intmax_t ratio<_Num, _Den>::num;

template<intmax_t _Num, intmax_t _Den>
constexpr intmax_t ratio<_Num, _Den>::den;
ratioには、2つの静的定数メンバー変数しかありません.
  • 分子num
  • 分母den
  • 次に、値を見てみましょう.
    static constexpr intmax_t num =
        _Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value;
    
    static constexpr intmax_t den =
        __static_abs<_Den>::value / __static_gcd<_Num, _Den>::value;
    

    分子分母には簡略化があり、簡略化の原理はそれらの最大公約数を同時に除くことである.
    また,ソースコードからratioが簡略化されると分母の負の番号が分子にシフトすることがわかる.
    したがって、クラス外には次の2つの変数の定義があります.
    template<intmax_t _Num, intmax_t _Den>
    constexpr intmax_t ratio<_Num, _Den>::num;
    
    template<intmax_t _Num, intmax_t _Den>
    constexpr intmax_t ratio<_Num, _Den>::den;
    
    ratioはコンパイル期間が安全であると以前に述べたが,これはC++11の新しい特性static_assertのおかげで,コンパイル期間は断言した.
    static_assert(_Den != 0, "denominator cannot be zero");
    static_assert(_Num >= -__INTMAX_MAX__ && _Den >= -__INTMAX_MAX__, "out of range");
    

    この2つの文は、コンパイル期間中に分母がゼロであるかどうか、数値がオーバーフローしているかどうかを確認します.ratioはこの場所で終わったようですが、コードに現れた__static_gcdなどはまだ説明されていません.この一連のソースコードを見てみましょう.
    まず、ここではtype_traitsの基石であるintegral_constantが大量に使われていることを宣言します.具体的にはC++11のtype_を参照してください.traitsの基石-integral_constant.これらはすべてintegral_constantの応用で、よく体得することができます.__static_sign
    コンパイル期間シンボルを求める
    template<intmax_t _Pn>
    struct __static_sign : integral_constant<intmax_t, (_Pn < 0) ? -1 : 1> { };
    
    __static_abs
    コンパイル期間は絶対値を求め,__static_signを用いてシンボルを求め,その後負の正の性質を用いて絶対値を得た.
    template<intmax_t _Pn>
    struct __static_abs : integral_constant<intmax_t, _Pn * __static_sign<_Pn>::value> { };
    
    __static_gcd
    コンパイル期間は2つの定数の最大公約数を求める.
    これは最も興味深いもので,テンプレートの再帰継承にユークリッド再帰アルゴリズムを加えて実現され,再帰の終点偏特化テンプレートはintegral_constant,最終__static_gcd<6, 4>::value == 2から継承される.
    template<intmax_t _Pn, intmax_t _Qn>
    struct __static_gcd : __static_gcd<_Qn, (_Pn % _Qn)> { };
    
    /// partial specialization
    template<intmax_t _Pn>
    struct __static_gcd<_Pn, 0> : integral_constant<intmax_t, __static_abs<_Pn>::value> { };
    
    template<intmax_t _Qn>
    struct __static_gcd<0, _Qn> : integral_constant<intmax_t, __static_abs<_Qn>::value> { };
    

    ここまで、ratioの紹介は本当に一段落したが、標準ライブラリはratioを定義し、ratioにレンガを追加する補助類も必然的にある.例えば、スコアは、C++11におけるratioコンパイル期間スコア(一)を詳細に説明するために、加算除算することができる.
    最後に、次の表があります.これは、標準ライブラリでユーザーが作成しやすいタイプの再定義です.
    名前
    たんい
    y o c t o yocto yocto
    1 1 , 000 , 000 , 000 , 000 , 000 , 000\cfrac{1}{1,000,000,000,000,000,000} 1,000,000,000,000,000,0001​
    f e m t o femto femto
    1 1 , 000 , 000 , 000 , 000 , 000\cfrac{1}{1,000,000,000,000,000} 1,000,000,000,000,0001​
    p i c o pico pico
    1 1 , 000 , 000 , 000 , 000\cfrac{1}{1,000,000,000,000} 1,000,000,000,0001​
    n a n o nano nano
    1 1 , 000 , 000 , 000\cfrac{1}{1,000,000,000} 1,000,000,0001​
    m i c r o micro micro
    1 1 , 000 , 000\cfrac{1}{1,000,000} 1,000,0001​
    m i l l i milli milli
    1 1 , 000\cfrac{1}{1,000} 1,0001​
    c e n t i centi centi
    1 100\cfrac{1}{100} 1001​
    d e c i deci deci
    1 10\cfrac{1}{10} 101​
    d e c a deca deca
    10 10 10
    h e c t o hecto hecto
    100 100 100
    k i l o kilo kilo
    1 , 000 1,000 1,000
    m e g a mega mega
    1 , 000 , 000 1,000,000 1,000,000
    g i g a giga giga
    1 , 000 , 000 , 000 1,000,000,000 1,000,000,000
    t e r a tera tera
    1 , 000 , 000 , 0000 , 000 1,000,000,0000,000 1,000,000,0000,000
    p e t a peta peta
    1 , 000 , 000 , 0000 , 000 , 000 1,000,000,0000,000,000 1,000,000,0000,000,000
    e x a exa exa
    1 , 000 , 000 , 0000 , 000 , 000 , 000 1,000,000,0000,000,000,000 1,000,000,0000,000,000,000
    コードは次のとおりです.
    typedef ratio<1,       1000000000000000000> atto;
    typedef ratio<1,          1000000000000000> femto;
    typedef ratio<1,             1000000000000> pico;
    typedef ratio<1,                1000000000> nano;
    typedef ratio<1,                   1000000> micro;
    typedef ratio<1,                      1000> milli;
    typedef ratio<1,                       100> centi;
    typedef ratio<1,                        10> deci;
    typedef ratio<                       10, 1> deca;
    typedef ratio<                      100, 1> hecto;
    typedef ratio<                     1000, 1> kilo;
    typedef ratio<                  1000000, 1> mega;
    typedef ratio<               1000000000, 1> giga;
    typedef ratio<            1000000000000, 1> tera;
    typedef ratio<         1000000000000000, 1> peta;
    typedef ratio<      1000000000000000000, 1> exa;