模板類未實例化時,為什麼編譯器會漏檢一些錯誤?
這是《深入探索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還得給來開洞因為這是 Visual C++ 的 Bug。如果你用 GCC 和 clang 試一下編譯這一段代碼,你會發現它們都給出了錯誤報告。相關概念是「二段式名字查找」(Two-phase name lookup):
- 編譯器初次邂逅模板聲明時:應該檢查所有「獨立名字」(non-dependent names),即不依賴模板參數的名字。
- 實例化模板時:此時模板參數已知,於是應該對「非獨立名字」(dependent names)進行檢查。
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 一樣在字元串首指明長度?