The C++ standard library(侯捷/孟巖 譯) 02--numeric limits/輔助函數及比較操作符

4. numeric limits/輔助函數/comparison operator

一般而言,數值型別(numeric types)的極值是與平臺有關的。

C++標準程序庫通過template numberic_limits提供這些極值,
    取代傳統C采用的預處理器常數(preprocessor constants)**,當然二者都可使用。

整數常數定義在<climits>和<limits.h>。
浮點數定義于<cfloat>和<float.h>

新的極值概念有兩個優點:提供了更好的型別安全型;可借此寫出一些template以核定(evaluate)這些極值。

note:C++ standard規定了各種型別必須保證的最小精度,如果能注意并運用這些價值就可寫出與平臺無關的程序。

p59_t4-1.png

4.1 class numeric_limits<>

使用template通常是為了對所有型別一次性的寫出一個通用解決方案。但還可以在必要時以template為每個型別提供共同接口。
方法:不但提供通用性template,還提供其特化(specialization)版本,eg:numeric_limits

1. 通用性template,為所有型別提供缺省值:
namespace std
{
    /* general numeric limits as default for any type
    */
    template <class T>
    class numeric_limits
    {
    public:
        //no specialization for numeric limits exist
        static const bool is_specialized = false;
        //.. //other members that are meaningless for the general numeric limits
    };
}
這個通用性template將成員is_specialized設為false,意思是對于型別T,無所謂極值的存在。

2. 各個具體型別的極值,由特化版本(specialization)提供:
namespace std
{
    /* numeric limits for int
    * - implementation defined
    */
    template <> class numeric_limits<int>
    {
    public:
        //yes, a specialization for numeric limits of int does exist
        static const bool is_specialized = true;

        static T min() throw()
        {
            return -2147483648;
        }
        static T max() throw()
        {
            return 2147483647;
        }
        static const int digits = 31;
        //...
    };
}

通用性numeric_limits template及其特化版本都放在<limits>頭文件中。
C++ standard所囊括的特化版本涵蓋了所有數值基本型別:
bool/char/signed char/unsigned char/wchar_t、
short/unsigned short/int/unsigned int/long/ float/double/long double

class numeric_limits<>所有成員及其意義如下圖


p62_t4-2-0.png
p62_t4-2-1.png

4.2 float型別數值限定模板特殊化的示例(page62)

namespace std
{
    template<> class numeric_limits<float>
    {
    public:
        //yes,a specialization for numeric limits for limits of float does exist
        static const bool is_specialized = true;
    
        inline static float min() throw()
        {
            return 1.17549435E-38F;
        }
        inline static float max() throw()
        {
            return 3.40282347E+38F;
        }

        static const int digits = 24;
        static const int digits10 = 6;
    
        static const bool is_signed = true;
        static const bool is_integer = false;
        static const bool is_exact = false;
        static const bool is_bounded = true;
        static const bool is_modulo = false;
        static const bool is_iec559 = true;

        static const int radix = 2;

        inline static float epsilon() throw()
        {
            return 1.19209290E-07F;
        }

        static const float_round_style round_style = round_to_nearest;
        inline static float round_error() throw()
        {
            return 0.5F;
        }

        static const int min_exponent = -125;
        static const int max_exponent = +128;
        static const int min_exponent10 = -37;
        static const int max_exponent10 = +38;

        static const bool has_infinity = true;
        inline static float infinity() throw() {         return ...;}
        static const bool has_quiet_NaN = true;
        inline static float quiet_NaN() throw() {         return ...;}
        static const bool has_signaling_NaN = true;
        inline static float signaling_NaN() throw() {         return ...;}
        static const bool has_denorm_loss = false;
        inline static float infinity() throw() {         return ...;}

        static const bool traps = true;
        static const bool tinyness_before = true;
    };
}

note:數據成員是const或static,如此其值便在編譯期確定;
      至于函數定義的成員,某些編譯器無法在編譯期間確定其值。
      故同一份代碼在不同處理器上執行可能得出不同浮點數。
p64.png

C++ standard保證,若denorm_absent為0則為false,若denorm_present為1且denorm_indeterminate為-1則二者都為true。故可把has_denorm視為一個bool以判斷某型別是否允許"denormalized values"。

4.3 numeric_limits<> 使用范例(page64)

/* 此例用于展示 某些型別極值的可能運用(eg了解某型別的最大值或確定char是否有符號)
*/
#include <iostream>
#include <limits>
#include <string>
using namespace std;

int main()
{
    // use textual representation for bool
    cout << boolalpha;

    // print maximum of integral type
    cout << "max(short): " << numeric_limits<short>::max() << endl;
    cout << "max(int): " << numeric_limits<int>::max() << endl;
    cout << "max(long): " << numeric_limits<long>::max() << endl;
    cout << endl;

    // print maximum of floating-point types
    cout << "max(float): " << numeric_limits<float>::max() << endl;
    cout << "max(double): " << numeric_limits<double>::max() << endl;
    cout << "max(long double): " << numeric_limits<long double>::max() <<endl;

    // print whether char is signed
    cout << "is_signed(char): " << numeric_limits<char>::is_signed << endl;
    cout << endl;

    // print whether numeric limits for type string exist
    cout << "is_specialized(string): " << 
            numeric_limits<string>::is_specialized << endl;
}

程序輸出結果與執行平臺有關,如下是一種可能:


limits.png

4.5 輔助函數/比較操作符(page66)

4.5.1 輔助函數

算法程序庫(定義于頭文件<algorithm>)內含3個輔助函數,一個是兩值取大,一個是兩值取小,第三個用于交換兩值。

1. 挑選較小值和較大值
namespace std
{
    template <class T>
    inline const T& min(const T& a, const T& b)
    {
        return b < a ? b : a;
    }
    template <class T>
    inline const T& max(const T& a, const T& b)
    {
        return a < b ? b : a;
    }
}
如果兩值相等,通常返回第一值(程序最好不要依賴這一點)。

另一版本:接受額外的template參數作為“比較準則”
namespace std
{
    template <class T , class Compare>
    inline const T& min (const T& a, const T& b,Compare comp)
    {
        return comp(b,a) ? b : a;
    }
    template <class T , class Compare>
    inline const T& max (const T& a, const T& b,Compare comp)
    {
        return comp(a,b) ? b : a;
    }
}
作為“比較準則”的參數應該是函數或仿函數,接受兩參數并比較:
    在某指定規則下,判斷第一個參數是否小于第二參數并返回判斷結果。
eg,code:
#include <algorithm>
using namespace std;

/*function that compares two pointers by comparing the values to which they point
 */
bool int_ptr_less (int* a, int* b)
{
    return *a < *b;
}

int main()
{
    int x = 17;
    int y = 42;
    int* px = &x;
    int* py = &y;
    int* pmax;

    // call max() with sepcial comparison function
    pmax = max (px, py, int_ptr_lesss);
    //...
}

2. 兩值互換
函數swap()用于交換兩對象的值。其泛型版定義于<algorithm>。
namespace std
{
    template <class>
    inline void swap(T& a, T& b)
    {
        T tmp(a);
        a = b;
        b = tmp;
    }
}

當且僅當swap()所依賴的copy構造函數和assignment操作行為存在時,這個調用才可能有效。
swap()的最大優勢:

透過template specialization或function overloading,可以為更復雜的型別提供特殊的版本:
    可交換對象內部成員而不是反復賦值,如此可節約時間。
    標準程序庫中所有容器都用了這項技術。eg:
        一個容器僅含一個array和一個成員(用于指示元素數量),那么其特化版本的swap()可以是:
class MyContainer
{
private:
    int* elems;  // dynamic array of elements
    int* numElems;  // numbers of elements
public:
    //...
    // implementation of swap()
    void swap(MyContainer& x) 
    {  
        std::swap(elems, x.elems);
        std::swap(numElems, x.numElems);
    }
    //...
    
    //overloaded global swap() for this type
    inline void swap(MyContainer& c1, MyContainer& c2)
    {
        c1.swap(c2);  // calls implementation of swap()
    }
}; 
4.5.2 輔助操作符(Comparison Operators)(page69)

有四個template functions,分別定義了!= 、>、<=、>=比較操作符,它們都是利用操作符== 和 <完成的。四個函數定義于<utility>。

namespace std
{
    namespace rel_ops
    {
        template <class T>
        inline bool operator!= (const T& x, const T& y)
        {
            return !(x == y);
        }

        template <class T>
        inline bool operator> (const T& x, const T& y)
        {
            return y < x;
        }

        template <class T>
        inline bool operator<= (const T& x, const T& y)
        {
            return !(y < x);
        }

        template <class T>
        inline bool operator>= (const T& x, const T& y)
        {
            return !(x < y);
        }
    }
}

只要加上using namespace std::rel_pos上述四個比較操作符就自動獲得了定義。

4.6 頭文件<cstddef>和<cstdlib> (page71)

4.6.1 <cstddef>各種含義
p71_t4-6.png
NULL通常表一個不指向任何對象的指針,即0(型別是int或long),但C中NULL常定義為(void*)0。
C++ 中沒定義從 void*到任何其他型別的自動轉型操作。
NULL也定義于頭文件<cstdio>/<cstdlib>/<cstring>/<ctime>/<cwchar>/<clocale>。
4.6.2 <cstdlib>內各種含義
p72_t4-7.png
常數EXIT_SUCCESS和EXIT_FAILURE用于作exit()的參數或main()的返回值。

經由atexit()注冊的函數,在程序正常退出是會依注冊的相反次序被一一調用,
    無論是通過exit()退出或main()尾部退出,都是如此,不傳遞任何參數。

exit()和abort()可在任意處終止程序運行,無需返回main()。

exit()會銷毀所有static對象,將所有緩沖區buffer清空,關閉所有I/O通道channels,
    然后終止程序(之前會先調用由atexit()注冊的函數),
      若atexit()注冊的函數拋出異常則會調用terminate()。

abort()會立刻終止函數且不做任何清理clean up工作。

note:這兩個函數都不會銷毀局部對象local objects,
    因為堆棧 輾轉開展動作stack inwinding 不會被執行。
    為確保所有局部對象的析構函數被調用,應用異常exceptions或正常返回機制,再由main()離開。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容