悲傷的 C++ throw(…)


當在C++語言中引入異常時,引入了相應的throw(…)動態異常說明符,注釋了哪些異常可以由函數拋出。比如:

// this function might throw an integer or a pointer to char,
// but nothing else.
void foo() throw(int, char*);

 這讓很多人非常憤怒,並被普遍認為是一個糟糕的舉動。

根據C++ 98標准,如果函數拋出了未在其動態異常說明符中指定的類型中列出的異常, 系統調用了std::unexpected()函數,std::unexpected()的默認行為是通過調用std::terminate()來終止程序。 作為特殊情況,throw()意味着函數根本不應該拋出任何異常。
在C++ 11標准里,放棄了throw(…)動態異常說明符,並且在C++ 17中,除了throw()的特殊情況之外,所有對動態異常說明符的支持都被刪除。 同時,改變了當你說你不會的時候拋出異常的懲罰:運行時直接調用std::terminate(),而不是通過std::unexpected()。
當然,微軟C++編譯器必須做一些不同的事情。

微軟C++編譯器將throw(…)異常說明符視為程序員的一個允諾,但沒有強制執行。它相信你會遵守你自己強加的合同。如果在函數承諾不拋出異常時拋出異常,則行為是未定義的。如果函數說可以拋出一些異常,編譯器不會驗證是否允許實際拋出的異常;它只是傳播異常。實際上,發生的情況是編譯器在假定不會引發不允許的異常的情況下執行優化。最常見的這種優化是,編譯器不必為它“知道”永遠不需要展開的事情注冊展開代碼,因為在對象銷毀之前,沒有可能拋出異常的點。

void Example()
{
   ObjectWithDestructor obj;
   obj.stuff_that_does_not_throw();
   // destructor runs here
}

如果stuff_that_does_not_throw被標記為不拋出,那么編譯器可以避免在異常傳播期間注冊obj進行展開,因為您承諾任何異常都不能逃逸。然后拋出異常並使所有優化無效。最常見的可見效果是,從不應該讓異常轉義的函數傳播的異常,以及某些對象析構函數無法運行。

但是等等,一切都沒有失去。如果啟用/std:c++17,那么微軟C++編譯器將實現throw(…)的標准行為。

 

 是的,到那兒花了很長時間,但遲到總比不到好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM