2012-02-16 30 views
11

以下是一段測試代碼,我將它與MSVC和Clang的編譯結果進行了比較。每個編譯器的輸出如下所示。 MSVC假裝未使用的模板聲明不存在。鏗鏘產生一個錯誤。問題是,哪個編譯器在這裏最符合標準?Clang vs MSVC:模板函數原型的處理

我已經看到了依賴於MSVC行爲的遺留生產代碼,我不確定它是否可以繼續依賴。

class S 
{ 
    struct P {}; 
}; 

template<typename T> 
S::P Bat(T); 

完全編譯在MSVC10:

E:\clangbuild\bin\Release>cl /c /nologo test.cpp 
test.cpp 

在鏘產生一個錯誤:

E:\clangbuild\bin\Release>clang++ test.cpp 
test.cpp:9:4: error: 'P' is a private member of 'S' 
S::P Bat(T); 
^
test.cpp:5:9: note: implicitly declared private here 
struct P {}; 
     ^
1 error generated. 
+2

你究竟打算「依賴」什麼?該模板不能被實例化。 – 2012-02-16 01:14:47

+0

我正在查看的代碼(但沒有寫入)使用了MSVC允許錯誤模板編譯來強制限制傳遞到其他模板的類型的晦澀事實。當傳入錯誤類型時,將使用使用此模板的重載,從而導致編譯錯誤。 – brendanw 2012-02-16 01:30:00

+0

@brendanw:即使是EDG前端也不會給出任何錯誤。 – 2012-02-16 08:51:51

回答

4

由於C++中的兩階段名稱查找,此操作失敗。

在第一階段,當模板被初始化之前,很久之前它被實例化,編譯器解析模板並查找任何非依賴名稱。 S::P是一個非依賴名稱,因此編譯器試圖查找它,但由於它是私有的而失敗。

在階段2中,當實例化模板時,編譯器將查找任何依賴名稱,這些名稱可能因模板而異。

Clang相當嚴格地符合兩階段名稱查找。但是,MSVC有一個模板解析模型,它將幾乎每次查找都延遲到實例化時間,這是階段2的一部分。這個延遲就是爲什麼你的示例將使用MSVC(不符合)編譯而不是在clang中編譯的原因。下面是詳細信息的鏈接:

The Dreaded Two-Phase Name Lookup

另外,這裏是從C++標準的部分,其中它描述了兩階段查找。

14.6.8:

當尋找一個模板 定義中使用的名稱的聲明,通常的查找規則(3.4.1,3.4.2)用於 非依賴的名字。推遲查找名稱取決於模板 參數,直到已知實際模板參數。

14.6.9:

如果名稱不依賴於一個模板參數(如 14.6定義。2)中,該名稱的聲明(或一組聲明)應在名稱出現在模板 定義中的點的範圍內;該名稱被綁定到在該點發現的聲明(或聲明) ,並且該綁定不受在實例化點可見的聲明 的影響。

然後適用於您3.4名稱查找的一部分:

訪問規則(第11條),被認爲是一次名字查找和 函數重載解析(如果適用)已經成功了。在名稱查找,函數重載解析(如果適用)和 訪問檢查成功之後只有 是 名稱聲明在表達式處理(第5節)中進一步使用的屬性。

從閱讀這些部分可以明顯看出你的程序不合格。標準狀態應該被推遲到實例化之前唯一的事情就是查找一個從屬名稱。非從屬名稱經歷通常的名稱查找,其中包括訪問規則。

+0

你的根源(+1!),但我不知道是否都是標準符合或不符合。標準不會確切地說明這個計劃是否不合規範。 – 2012-02-16 07:18:23

+0

訪問檢查不是名稱查找的一部分。 MSVC(2010年至少)確實對模板聲明中的所有非依賴名稱進行名稱查找;在發佈的示例中將S :: P更改爲S :: A,它將引發錯誤。 – Gerald 2012-02-16 08:24:59

+0

@Gerald MSVC將檢查名稱是否存在,但名稱解析和考慮訪問控制是名稱查找的一部分。請參閱:http://stackoverflow.com/a/6273465/375343 – 2012-02-16 13:23:29

3

只真正需要編譯器檢查的未初始化模板聲明任何語法錯誤。任何額外的語義評估只需要在模板函數實例化時完成。

由於S :: P確實是一個有效從函數返回的類型,它們都是同等符合的。

+0

那麼,說我們僅僅處於由編譯器作者決定並且未被標準定義的任意級別錯誤檢查的領域是否公平? – brendanw 2012-02-16 01:44:59

+0

@Gerald現在的問題是:你在哪裏閱讀標準?我一直在尋找一個小時,但沒有成功。 – Klaim 2012-02-16 01:50:39

+0

@Brendanw - 是的。就C++標準而言,這個函數不存在。但是編譯器可以自由地執行額外的錯誤檢查。就標準而言,所有真正重要的是,當函數模板被實例化時,它是無效的。在你的問題下的註釋中描述的場景中,當一個錯誤類型被傳遞給其他模板時,函數模板將被隱式聲明,這會導致錯誤。這是符合標準的行爲,其餘依賴於編譯器。 – Gerald 2012-02-16 01:51:42