作者:陳甫鸼
來源:知乎,可點擊閱讀原文
你們公司允許使用C++STL嗎?各位老鐵可以在我價值¥2700的評論區留言。

最初開始禁用 C++ STL,是因為早期項目編碼實踐中留下的慣例,被后來的程序員繼承下來。老項目中這種選擇尤其地多。不過如果有人將其上升到公司行為在不同項目中全面禁用 STL,則沒有必要,而且我傾向于做這種決定的人并不理解 C++ 編譯系統。
一般來說,項目中禁用 C++ 多見于兩種具體場景:項目的產出產品為函數庫 OR 需要引用第三方函數庫。
具體地來說,有三個主要原因:
第一個原因是二進制邊界混亂。對需要在項目中使用第三方函數庫的程序員來說,二進制邊界是個頭痛的問題。C++ 在這一方面本身就處理得不算好,加上模板后起到的是雪上加霜的后果。沒有經驗的程序員會貪圖方便而在公開頭文件中使用 C++ 模板,如果這時調用方的編譯器選項設置或 STL 版本和編譯方不同,那么就可能出現同樣的頭文件在不同的環境下二進制布局不符的情況。
順便說一句,在過去十年里,各個主流編譯器附帶的 STL 版本變化節奏不慢,所以這種由于編譯環境不同而導致的 bug 并不算罕見,但缺乏匯編知識的用戶難以排查。
第二個原因是不愿使用異常。如今除了 Android 上的 STLPort 關閉異常,大部分主流 C++ STL 實現里都無法脫離異常使用 STL。異常帶來的問題主要是兩個:性能下降,代碼膨脹。這幾年 C++ 編譯器在性能方面的改進很多,good path 的性能問題已經基本沒有,但代碼膨脹問題卻沒有太多改善,甚至這個性能問題的一部分解決方案就是以代碼膨脹為代價。我寫過一篇短文比對過 Android 上 gcc 4.6 在有無異常的情況下的匯編代碼邏輯,可以看到,啟動異常時生成的匯編代碼量多出了相當一部分(我的例子中是 50%),用于處理各種隱含代碼中的異常問題。這一條在手機系統中有時候會引起意想不到的麻煩,比如軟件升級后導致 app 在低存儲容量的手機中安裝失敗。順便說一句,這個問題并不是 gcc 獨有,clang 上生成的代碼是一樣的。參考:http://dummydigit.net/posts/2014-01-01-23-30-1.html
最后一個原因是 C 兼容。嚴格地說,STL 在這個問題上算是躺槍,這個坑在很多具體的場景中也是因為異常而引入,但這個問題的麻煩程度比前兩個問題更高。比如 gcc 在編譯純 C 代碼時默認關閉 -fexceptions 選項,因此這樣編譯出來的代碼中沒有異常處理相關的棧展開。如果某個 C++ 項目引用了一個第三方 C 項目,它很難確保那個 C 項目給出的二進制代碼中正確進行了異常處理并保證代碼服從異常安全操作。這種場景下混用 C/C++ ,就可能在拋出異常時莫名其妙地崩潰或者出現 C 代碼區段中的資源泄漏,特別是 expat 那種大量利用回調的代碼結構。要規避這種風險并非不可能,但需要 C 的架構部分做修改,比如使用 DOM 那種樹形結構,這種做法對歷史項目而言又很難辦到。換言之,如果一個項目出于種種原因需要保持 C 兼容,而 STL 就屬于其中一個不可控的變數,與其相信程序員不犯錯,不如直接禁用更可控一些。參考:Code Gen Options
要解決二進制相關的問題很簡單:整個項目的所有相關代碼在同一個代碼基上編譯,強制打開編譯選項添加異常代碼,并去除一切二進制依賴。但對很多小公司來說,引入這樣的系統對配置管理的要求較高。如果一部分依賴關系來自自己并不了解的第三方代碼,輕易修改編譯選項可能帶來的風險與第三方代碼庫的規模成正比。退一步說,即便團隊里真的有強大的配置管理工程師能夠搞定一切,他們也不會有能力解決代碼膨脹問題,除非他們有權決定換一個編譯器。相比之下,前面朋友所說的所謂性能或者編譯出錯時糟糕的可讀性,在我看來反倒是次要因素,而且這些缺陷都正在新的編譯器中逐步得到解決或改善,比如 clang。
往期推薦