標籤:

世界上存在「與規範完全一致」的 C++ 編譯器/解釋器嗎?


C++有很多的特性,標準條款也有很多,設計一個完全符合標準的C++編譯器是一件非常非常困難的事情。

與此同時,C++標準也有很多未定義的行為,即交給編譯器自己去決定。而這個時候就會出現編譯器設計人員理解不同的情況了,於是就會有不同編譯器不同表現行為。然而,很多時候程序員往往會以他熟悉的編譯器作為基準,若其它編譯器與這個編譯器表現不一致就說其它編譯器有Bug,不符合標準,這其實是一種誤解。很多時候,其它編譯器在某個平台上的策略是盡量兼容這個平台的主流編譯器,如編譯選項,C++ Object Model等,所以我們經常會看見在Linux平台下的編譯器都會宣稱是否兼容GCC編譯器,或者說兼容了GCC編譯器多少。而在Windows平台下,則是是否兼容VC++編譯器,如Intel C++編譯器就是一個例子。

而除掉未定義的行為,一些C++特性實現起來也是極為繁瑣與困難,如C++14 constexpr,模板等。很多情況下,小的例子也是完全體現不出來的,當特性一綜合很容易就崩了,這一點在模板方面體現的尤為突出。所以,我們即使作為編譯器開發者,測試一些特性的時候也是以通過某某Test Buckets達到多少通過率就可以認為是合格了,比如對Boost的測試,目前幾乎所有的編譯器都是95%左右的通過率。

所以,這需要怎麼看待這個完全符合規範一說,非要嚴格的說,如要求100%支持並且通過相關測試集,以我目前的認知來看,很遺憾,沒有。


首先定義一下討論的範疇。這裡只談主流C++編譯器。如果有人自製了一個C++編譯器,小範圍試用,不在討論之列。

主流C++編譯器裡面,唯一一個實現了完整的C++98的,包括export關鍵字,只有Comeau C++(見Mark S. Joshi (2008). C++ Design Patterns and Derivatives Pricing. Cambridge University Press. p. 263. ISBN 978-0-521-72162-2)。而它在08年以後就不活動了。

C++11來說,MSVC、GCC和Clang對其的支持都是不錯的。但MSVC不支持SFINAE,GCC和Clang不支持Minimal GC(雖然MSVC也是個空實現)。但這只是在feature的層面。至於是否完全遵循規範,都很夠嗆。


程序都是有 bug 的。規範經過不同組織解釋都是有差異的。工程投入都是有限的。從這三點來看,存在完全符合標準的東西幾乎不可能。


題主說的的C++規範是哪一版呢?C++不同規範版本實在是差太遠了,不說明是哪一版實在是很難回答這樣的問題。

不過話又說回來,就算是古老的C++98/03,規範里也還有export這種世界上99%的編譯器都不支持的關鍵詞。


這個問題雖然我沒有確切地證實過,不過我大膽地猜測答案肯定是:不存在。

因為C/C++ standard規定了一些說起來很輕鬆,實現起來很費勁,又沒什麼人在乎的東西。

C++就不說了,自從引入了Template,compiler bug就層出不窮,雖然也沒什麼人在乎。

哪怕是C的compiler都沒法做的完全符合標準。

舉個例子,既然有人提到GNU了。

C standard里有個函數叫做mktime(),在time.h里。這個函數把一個struct tm變成一個秒數。c99 standard規定,如果struct tm裡面(一個struct裡面寫的年份,月份,天數之類的)的數據是不合法的,要把他變成合法,然後計算。比如我寫69年12月和70年-1月效果是一樣的。然後我寫8點0分和9點-60分是一樣的。

好了,我們都知道mktime絕大部分實現是通過小時*3600+分鐘*60+秒實現最後的秒數計算的。換言之如果小時×3600溢出了超過2個time_t,這個秒數就基本肯定錯了(絕大部分情況是報溢出)。

比如小時=INT_MAX/60, 分鐘=(INT_MAX/60)*60。理論上說這個結果應該和小時分鐘都為0是一樣的。但是實際計算結果不是。這就是不符合standard的地方。(測試平台ubuntu 14.04,gnu4.8.2,with -m32 on 64-bit OS,如果time_t是64bit,就木有這個問題了。不過我猜這個問題你應該找不到compiler解決了)。


所謂規範,本來就是幾個開發商做下來相互扯皮的結果,我做了功能A,你做了功能B,他做了功能C,然後我們博弈一下,AB聯合坑了C,AB都進標準,回家各自再完善。C被宣布為非法,回家捲鋪蓋……


在沒有形式化規範的前提下,是沒法嚴格的判斷一個編譯器實現的語言是否滿足語言的規範,最多只能通過不斷的測試做近似判斷。有了形式化規範,才有可能通過形式化方法構造和證明一個滿足規範的編譯器,例如 compcert。但是即使是compcert這樣只實現了一個 c 的子集的編譯器,證明其滿足規範也不是一件容易的事,至少證明所花費的人月是遠遠大於目前實際使用的編譯器的開發開銷的。退一步講構造形式化語言規範:對於c++這樣從頭至尾沒有考慮過形式化且又如此複雜的語言,回過頭來從其非形式化規範構造形式化規範是一件幾乎不可能的事情 (但是學術界仍然有人在不斷努力),更勿論非形式化規範中可能的前後矛盾。


可以用窮舉法來判斷:

主流的c++編譯器就那麼幾家,都存在不支持的C++特性,不過有多有少而已

非主流的、專用的c++編譯器數量不好估計,但是在全面、標準方面一般比不上主流編譯器,因此也不完全支持c++標準。

結論:傳統意義上的c++編譯器都不能完全符合C++標準。

但是,要開發一個完全符合C++標準的編譯器也是可以的。如果對編譯速度沒有要求的話,我就可以做。只需要提供:

1、要支持的C++標準的完整描述

2、一個較大程度上支持該標準的主流C++編譯器

3、可以理解該C++標準、以及熟悉該主流C++編譯器的人

我的編譯器的做法:

1、編譯器運行後彈個窗,輸入代碼或代碼的壓縮包

2、編譯器將代碼傳回後台,由那個人用那個已有的編譯器編譯代碼

3、編譯不通過的地方由人修改代碼或轉為其他形式實現,使得代碼編譯通過

4、將編譯結果傳回前台

這樣,一個效率不太高的人肉雲編譯器就做成了,完全符合C++標準。


推薦閱讀:

編譯器內部是如何處理 C 語言 typedef 關鍵字的?
C++ 中的 std::vector 為什麼可以越界訪問?
以後的C++編譯器有沒有可能使用C#來編寫?
編譯器或解釋器前端對於作用域的處理方法?
GitHub 上有沒有什麼簡單精緻的編譯器源碼適合初學者研讀??

TAG:C | 編譯器 |