<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## numeric_cast ### 頭文件: `"boost/cast.hpp"` 整數類型間的轉換經常會產生意外的結果。例如,`long` 可以擁有比`short`更大范圍的值,那么當從 `long` 賦值到`short` 并且 `long`的數值超出了 `short`的 范圍時會發生什么?答案是結果是由實現定義的(比"你不可能明確知道"好聽一點的說法)。相同大小整數間的有符號數到無符號數的轉換是好的,只要有符號數 的數值是正的,但如果有符號數的數值是負的呢?它將被轉換為一個大的無符號數,如果這不是你的真實意圖,那么就真的是一個問題了。`numeric_cast` 通過測試范圍是否合理來確保轉換的有效性,當范圍超出時它會拋出異常。 在我們全面認識 `numeric_cast`之前,我們必須弄清楚支配整數類型的轉換及提升的規則。規則有很多并有時很微妙,即使是經驗豐富的程序員也會被它們欺騙。與其寫出所有這些規則\[7\]并展開它們,我更愿意給出一些有關轉換的例子,它們會引起未定義或令人驚訝的行為,然后再解釋所使用的轉換規則。? ??? ??? ??? [7].?C++標準在§4.5-4.9中討論數字類型的提升及轉換。 當從一種數字類型賦值給另一種數字類型的變量時,就會發生類型轉換。在目標類型可以保存源類型的所有數值的情況下,這種轉換是完全安全的,否則就是不安全的。例如,`char` 通常不能保存`int`的最大值,所以當從`int`到`char`的賦值發生時,很大可能`int`的值不能被表示為`char`. 當類型可以表示的數值范圍不同時,我們必須確認用于轉換的實際數值在目標類型的有效范圍之內。否則,我們就會進入實現定義行為的范疇;那就是在把一個超出數字類型可能的數值范圍的值賦給這個數字類型時會發生的事情。\[8\] 實現定義行為意味著具體實現可以自由地做任何它想做的;不同的系統可能有完全不同的行為。`numeric_cast` 可以確保轉換是有效的、合法的,否則就不允許轉換。 > \[8\] 無符號數也算,盡管它的行為是有定義的。 ### 用法 `numeric_cast` 是一個看起來象C++的轉型操作符的函數模板,它泛化了目標類型及源類型。源類型可以從函數的參數隱式推導得到。使用`numeric_cast`, 要包含頭文件`"boost/cast.hpp"`。以下兩個轉換使用 `numeric_cast` 安全地將 `int` 轉換為 `char`, 以及將 `double` 轉換為 `float`. ``` char c=boost::numeric_cast<char>(12); float f=boost::numeric_cast<float>(3.001); ``` 一個最常見的數字轉換問題是將來自一個更寬范圍的值賦給范圍較窄的類型。我們來看看[numeric_cast](../Text/content.html#ch02lev1sec4)如何幫忙。 ### 從較大的類型到較小類型的賦值 從較大的類型(例如`long`)向較小的類型(例如`short`)賦值,有可能數值過大或過小而不能被目標類型所表示。如果這發生了,結果是(是的,正如你猜到的)實現所定義的。我們稍后將討論無符號類型的潛在問題;我們先從有符號類型開始。C++中有四個內建的有符號類型: * `signed char` * `short int (short)` * `int` * `long int (long)` 沒有人可以絕對肯定哪個類型比其它的大\[9\],但典型地,上面的列表是按大小遞增的,除了 `int` 和 `long` 通常具有相同的值范圍。但它們都是獨立的類型,即使是有相同的大小。想查看你的系統上的類型大小,可以使用 `sizeof(T)` 或 `std::numeric_limits&lt;T&gt;::max()` 和 `std::numeric_limits&lt;T&gt;::min()`. > \[9\] 當然,有符號類型與無符號類型的范圍是不同的,即使它們有相同的大小。 當把一個有符號整數類型賦給另一個時,C++標準說: > "若目標類型為有符號類型,在數值可以被目標類型表示時,值不改變;否則,值為實現定義。"\[10\] > > &gt; [10] 見C++標準 §4.7.3? 以下代碼段示范了看起來象是正確的賦值是如何導致實現定義的數值,最后看看如何通過`numeric_cast`的幫助避免它們。 ``` #include <iostream> #include "boost/cast.hpp" #include "boost/limits.hpp" int main() { std::cout << "larger_to_smaller example\n"; // 沒有使用numeric_cast的轉換 long l=std::numeric_limits<short>::max(); short s=l; std::cout << "s is: " << s << '\n'; s=++l; std::cout << "s is: " << s << "\n\n"; // 使用numeric_cast的轉換 try { l=std::numeric_limits<short>::max(); s=boost::numeric_cast<short>(l); std::cout << "s is: " << s << '\n'; s=boost::numeric_cast<short>(++l); std::cout << "s is: " << s << '\n'; } catch(boost::bad_numeric_cast& e) { std::cout << e.what() << '\n'; } } ``` 通過使用 `std::numeric_limits`,?`long l` 被初始化 `short` 可以表示的最大值。該值被賦給 `short s` 并輸出。然后,`l` 被加一,這意味著它的值不能再被`short`所表示;它超出了 `short` 所能表示的范圍。把 `l` 的新值賦給 `s`, `s` 再次被輸出。你可能要問輸出的值是什么?好的,因為賦值的結果屬于實現定義的行為,這取決于你使用的平臺。在我的系統中,使用我的編譯器,它變成了一個大的負值,即它被回繞了。必須運行前面的代碼才知道在你的系統中會有什么結果\[11\]。接著,再次執行相同的操作,但這次用了 `numeric_cast`. 第一個轉型成功了,因為數值在范圍之內。而第二個轉型卻會失敗,結果是拋出一個 `bad_numeric_cast` 異常。程序的輸出如下。 > \[11\] 這種行為和結果在32位平臺上十分常見。 ``` larger_to_smaller example s is: 32767 s is: -32768 s is: 32767 bad numeric cast: loss of range in numeric_cast ``` 比避開實現定義行為更為重要的是,`numeric_cast` 幫助我們避免了錯誤,否則會很難捕捉到這些錯誤。那個奇怪的數值可能被傳送到應用程序的其它部分,程序可能會繼續工作,但幾乎可以肯定將產生錯誤的結果。 當然,這僅對于特定的數值會發生這樣的情況,如果這些數值很少出現,那么錯誤將很難被發現。這種錯誤非常陰險,因為它們僅僅對某些特定值會發生,而不是總會發生。 精寬或取值范圍的損失并不常見,如果你不確定一個值對于目標類型是否過大或過小,`numeric_cast` 就是你可以使用的工具。你甚至可以在不需要的時候使用 `numeric_cast` ;維護的程序員可能沒有象你一樣的洞察力。注意,雖然我們在這里只討論了有符號類型,但同樣的原理可應用于于無符號類型。 ### 特殊情況:目標類型為無符號整數 無符號整數類型有一個非常有趣的特性,任何數值都有可以合法地賦給它們!對于無符號類型而言,無所謂正或負的溢出。數值被簡單地對目標類型最大值加一取模。什么意思?看看以下例子會更清楚一些。 ``` #include <iostream> #include "boost/limits.hpp" int main() { unsigned char c; long l=std::numeric_limits<unsigned char>::max()+14; c=l; std::cout << "c is: " << (int)c << '\n'; long reduced=l%(std::numeric_limits<unsigned char>::max()+1); std::cout << "reduced is: " << reduced << '\n'; } ``` 運行這個程序的輸出如下: ``` c is: 13 reduced is: 13 ``` 這個例子把一個明顯超出`unsigned char`可以表示的數值賦給它,然后再計算得到同樣的數值。賦值的動作可以用這一行代碼來示范: ``` long reduced=l%(std::numeric_limits<unsigned char>::max()+1); ``` 這種行為通常被稱為數值回繞(value wrapping)。如果你想用這個特性,就沒有必要在這種情況下使用 `numeric_cast`。此外,`numeric_cast` 也不接受它。`numeric_cast`的意圖是捕捉錯誤,而錯誤應該是因為用戶的誤解而引起的。如果目標類型不能表示賦給它的數值,就拋出一個 `bad_numeric_cast` 異常。因為無符號整數的算法是明確定義的,不會引起程序員的重大錯誤\[12\]。對于 `numeric_cast`, 重要的是確保獲得實際的數值。 > \[12\] 觀點是:如果你真的想要數值回繞,就不要使用 `numeric_cast`. ### 有符號和無符號整數類型的混用 混用有符號和無符號類型可能很有趣\[13\], 特別是執行算術操作時。普通的賦值也會產生微妙的問題。最常見的問題是將一個負值賦給無符號類型。結果幾乎可以肯定不是你原來的意圖。另一種情形是從無符 號類型到同樣大小的有稱號類型的賦值。不知什么原因,人們總是會很容易忘記無符號類型可以持有比同樣大小的有符號類型更大的值。特別是在表達式或函數調用 中更容易忘記。以下例子示范了如何通過`numeric_cast`來捕捉這種常見的錯誤。 > \[13\] 當然這是一個高度主觀的問題,你的觀點可能不同。 ``` #include <iostream> #include "boost/limits.hpp" #include "boost/cast.hpp" int main() { unsigned int ui=std::numeric_limits<unsigned int>::max(); int i; try { std::cout << "Assignment from unsigned int to signed int\n"; i=boost::numeric_cast<int>(ui); } catch(boost::bad_numeric_cast& e) { std::cout << e.what() << "\n\n"; } try { std::cout << "Assignment from signed int to unsigned int\n"; i=-12; ui=boost::numeric_cast<unsigned int>(i); } catch(boost::bad_numeric_cast& e) { std::cout << e.what() << "\n\n"; } } ``` 輸出清晰地表明了預期的錯誤。 ``` Assignment from unsigned int to signed int bad numeric cast: loss of range in numeric_cast Assignment from signed int to unsigned int bad numeric cast: loss of range in numeric_cast ``` 基本的規則很簡單:無論何時在不同的類型間執行類型轉換,都應該使用 `numeric_cast`來保證轉換的安全。 ### 浮點數類型 `numeric_cast` 不能幫助我們在浮點數間的轉換中避免精度的損失。原因是`float`, `double`, 和 `long double`間的轉換不象整數類型間的隱式轉換那樣敏感。記住這點很重要,因為你可能會認為以下代碼應該拋出異常。 ``` double d=0.123456789123456; float f=0.123456; try { f=boost::numeric_cast<float>(d); } catch(boost::bad_numeric_cast& e) { std::cout << e.what(); } ``` 運行這段代碼不會有異常拋出。在許多實現中,從 `double` 到 `float` 的轉換都會導致精度的損失,雖然C++標準沒有保證會這樣。我們所能知道的就是,`double` 至少具有 `float` 的精度。 從浮點數類型轉為整數類型又會怎樣呢?當一個浮點數類型被轉換為一個整數類型,它會被截斷;小數部分會被扔掉。`numeric_cast` 對截斷后的數值與目標類型進行相同的檢查,就象在兩個整數類型間的檢查一樣。 ``` double d=127.123456789123456; char c; std::cout << "char type maximum: "; std::cout << (int)std::numeric_limits<char>::max() << "\n\n"; c=d; std::cout << "Assignment from double to char: \n"; std::cout << "double: " << d << "\n"; std::cout << "char: " << (int)c << "\n"; std::cout << "Trying the same thing with numeric_cast:\n"; try { c=boost::numeric_cast<char>(d); std::cout << "double: " << d; std::cout << "char: " << (int)c; } catch(boost::bad_numeric_cast& e) { std::cout << e.what(); } ``` 象前面的代碼那樣進行范圍檢查以確保有效的賦值是一件令人畏縮的工作。雖然規則看起來很簡單,但是有很多組合要被考慮。例如,測試從浮點數到整數的代碼看起來就象這樣: ``` template <typename INT, typename FLOAT> bool is_valid_assignment(FLOAT f) { return std::numeric_limits<INT>::max() >= static_cast<INT>(f); } ``` 盡管我已經提起過在一個浮點數類型被轉換時,小數部分會被丟棄,在這個實現中還得很容易忽略這個錯誤。這對于算術類型的轉換和提升是自然的。去掉 `static_cast` 就可以正確地測試,因為這樣 `numeric_limits&lt;INT&gt;::max` 的結果會被轉換為浮點數類型\[14\]。如果是浮點數類型轉為整數類型,它會被截斷;換句話說,這個函數的問題在于丟失了小數部分。 > \[14\] 這是正常的算術轉換結果。 ### 總結 `numeric_cast` 提供了算術類型間高效的范圍檢查轉換。在目標類型可以持有所有源類型的值時,使用 `numeric_cast`沒有額外的效率代價。它只在目標類型僅能表示源類型的值的子集時有影響。當轉換失敗時,`numeric_cast` 通過拋出一個 `bad_numeric_cast`異常來表示失敗。對于數值類型間的轉換有很多復雜的規則,確保轉換的正確性是很重要的。 以下情況時使用 `numeric_cast`: * 在無符號與有符號類型間進行賦值或比較時 * 在不同大小的整數類型間進行賦值或比較時 * 從一個函數返回類型向一個數值變量賦值,為了預防該函數未來的變化 在這里注意到一個模式了嗎?模仿已有的語言和庫的名字及行為是簡化學習及使用的好方法,但也需要仔細 地考慮。增加內建的C++轉型就象沿著狹窄的小路行走;一旦迷路會帶來很高的代價。遵循語言的語法及語義規則才是負責任的。事實上,對于初學者,內建的轉 型操作符與看起來象轉型操作符的函數可能并沒有不同,所以如果行為錯誤將會導致災難。`numeric_cast` 有著與 `static_cast`, `dynamic_cast`, 和 `reinterpret_cast`類似的語法和語義。如果它看起來和用起來象轉型操作,它就是轉型操作,是對轉型操作的一個良好的擴展。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看