模板類未實例化時,為什麼編譯器會漏檢一些錯誤?

這是《深入探索C++對象模型》書中的一個例子

template &
class Mumble
{
public :
Mumble(T t = 1024) : _t(t)
{
if (tt != t)
throw std::exception("no equal") std::exception("no equal")
}
private:
T tt;
};

這時,模板類中有3個明顯的錯誤:1,未聲明的成員變數_t;2,throw後面跟著兩個exception標示符;3,throw未有";"號結束。但在程序中未實例化該模板類時,我用vs2010編譯,就會編譯成功。

這三個錯誤在我看來都和模板類的套用類型T無關,不會因為不同的T就導致錯誤不存在,那麼是什麼原因,導致編譯器無法查找到錯誤呢?


因為 VS2010 不符合標準。try clang:

[Wandbox]三へ( へ?? ?)へ ????


截至我發出這段回答前,除了劉雨培聚聚之外其他人的回答都是錯誤的

C++標準要求對於模板的編譯要有兩個階段的語法檢查,簡單說:

第一個階段,在模板實例化之前,進行所有和模板參數無關的代碼的語法檢查,也就是所謂的non-dependent name lookup,此時就應該檢查出題主代碼中拋異常部分的代碼錯誤。

第二個階段,對模板實例化後進行餘下的和模板參數有關部分的語法檢查,也就是dependent name lookup,此時如果你的模板參數T缺少什麼成員類型啊,對象不存在某個成員啊這類錯誤會被提示出來。

而VS至今,對沒錯,VS2015和VS"15,對於模板的處理依然是不標準的,他的第一階段什麼都不幹,第二階段在模板實例化之後才進行所需的檢查,你這裡模板根本沒有實例化,所以也就沒有語法檢查了。

哦順便說微軟一大票模板庫都是按VS不標準的模板來寫的,搞的clang還得給來開洞

感人

再黑一波,MSVC最初的C編譯器做了一堆黑科技優化,運行時只需佔用170多KB的內存,就可以編譯C代碼,然後這些黑科技逐漸積累下來,現在爽了,只能一點一點重構


因為這是 Visual C++ 的 Bug。如果你用 GCC 和 clang 試一下編譯這一段代碼,你會發現它們都給出了錯誤報告。

相關概念是「二段式名字查找」(Two-phase name lookup):

  1. 編譯器初次邂逅模板聲明時:應該檢查所有「獨立名字」(non-dependent names),即不依賴模板參數的名字。
  2. 實例化模板時:此時模板參數已知,於是應該對「非獨立名字」(dependent names)進行檢查。

據說:clang 在設計時就是依照完全支持二段式名字查找設計的;GCC 次之;而蛋疼的 MSVC 則是把模板的幾乎所有名字查找工作都推遲到了第二階段。

C++11 §14.6/9

When looking for the declaration of a name used in a template definition, the usual lookup rules are used for non-dependent names. The lookup of names dependent on the template parameters is
postponed until the actual template argument is known.


除了這些,你在cl下還會發現:

1,非類型模板參數包有時候無法展開。

2,模板里定義的模板無法查找和實例化。

3,極端條件下兩個模板參數包無法交替展開((a,b...)...)。

4,單個不定參數模板多次實例化時相互干涉。

5,多層嵌套下模板別名無法作為模板的模板類參數。

待續


既然沒實例化,我是編譯器我連看都不看。。。反正用不著。。。

不過如果編譯器看了,分析了,但沒找出問題,那就妥妥是編譯器的問題。。。。


上面的答案都提到了,確實是MSVC沒有實現 two-phase lookup從而導致這個和標準不同。MSVC實現早於C++標準,而在標準之前誰也不知道需要怎麼解析template。GCC估計也會是由於這個原因開始沒實現,不過GCC 3.4還是實現了。C++標準成型後要考慮兼容老代碼不好改動,不過聽說VS2017會實現。之後就會有一致的結果了。

https://gcc.gnu.org/gcc-4.7/changes.html這裡提到GCC 4.7才正確實現了 two-phase lookup。


沒有實例化,編譯器壓根就不去編譯模板的代碼.


做個不恰當的比喻

就好象你define了一堆亂七八糟的東西沒有使用一樣。


見C++ Primer 5 16.1.1最後一個小標題


雖然微軟的編譯器有些地方不符合標準,但這裡是沒有問題的,

C++標準規定了對於沒有實例化的模板可以不檢查:no diagnostic required, if no valid specialization can be generated for a template


推薦閱讀:

C++/C/JAVA/Python之間的區別?
什麼樣的編程語言會不支持遞歸呢?
學編程是否應該堅持看英文版著作?
在程序開發中,++i 與 i++的區別在哪裡?
為什麼 C 語言對字元串的設計是用零結尾,而不是像 Pascal 一樣在字元串首指明長度?

TAG:編程語言 | 模板 | C | 編譯器 |