大改版,因爲(在評論)海報解釋了爲什麼& T:富;不是問題。
此警告的起源有點愣神複雜。
經過調查,我們發現,從微軟,ComicSansMS還貼出了以下內容:
當模板函數被實例化,在定義模板時編譯指示狀態確定其是否託管或非託管。
這是爲功能模板,而不是像您使用的類模板。
事實上,這個函數模板實例是有關的一個謹慎的方式。但是這種警告行爲的獨特方式與編譯過程有關。
編譯器實際上是生成託管代碼,用於調用和使用UsesFunPtr ctor,當您使用#pragma managed結束文件時。它給出了一個警告,其中有一些非託管代碼。這是對原因的深入分析。
Thunks基本上是圍繞某些vtable調用的包裝函數;維基百科有關該主題的體面文章。你生成一個thunk的原因是因爲你正在使用一個函數的地址(& T :: Foo)。
如果你的文件的最後一行是這樣的:
#pragma unmanaged
就會停止抱怨,即使你已經在該對象混合託管和非託管代碼。這是因爲前端和後端之間的「混淆」事物之間的斷開:它將編譯我上面用最後一個#pragma命令所述的代碼。
如果您會注意到,此警告不是編譯時警告。如果您修訂DOIT()函數像這樣它來編譯,當它說後「生成代碼...」:
void DoIt()
{
long a;
long long b;
b = 4;
a = b;
UsesFunPtr<Interface> d;
}
在「編譯x.cpp」你會得到一個關於截斷警告,則它會轉移到代碼生成階段(生成代碼...),在那裏它會給出這個問題的警告。
編譯器是解析等的前端,並創建一種中間二進制格式,類似於Java字節碼。代碼生成器是後端,通過此字節碼在此目標平臺上創建實際輸出(本例中爲Windows x86)。
直到後端獲得代碼後才進行優化。 thunk是優化的一種形式,因此它是發出警告的代碼生成器(取得半編譯的IL代碼),而不是編譯器。這不是一種可以以任何我知道的方式關閉的優化,因爲這是一種標準做法。
然而,編譯器實例化該模板;後端只看到一個完整的課程,並被告知要理解它。
編譯託管C++/CLI時有所不同。有時編譯器能夠使用所謂的鏈接時代碼生成。發生這種情況時,鏈接器是調用後端的鏈接器;當它不可能時(出於各種原因),它經歷了編譯的通用過程(前端 - >後端 - >鏈接器)。
#pragma按照收到的順序傳入IL。這似乎表明UsesFunPtr的函數支持代碼在被管理的#pragma發生後實際上是「追加到最後」。所以,即使你的代碼是在非託管空間,代碼生成器認爲:
- 非託管
- 對象定義,用
- 管理
- 額外的支持代碼的類,你沒有寫,不能看到與其他目標文件接口有關:創建和複製vtable等等等等。
發電機組沒有辦法來區分,如果你意味着UsesFunPtr構造函數,甚至類,是完全託管或非託管代碼,因爲它得到的IL並沒有真正連接兩個。它看到一個函數創建它是非託管的(通過編譯指示),並支持在其上運行的函數並在託管空間中生成thunk(通過編譯指示)。它無法分辨連接。既然你把那個#pragma放在那裏,它只是繼續看到它看到的最後一個#pragma。生成的支持代碼被管理。
你會發現,如果你在最後放的#pragma非託管,即使有管理配的模板代碼,你將不會得到警告。這是因爲它將該代碼編譯爲非託管,因此thunk不是問題。
如何總結這一切嗎?最終被管理的#pragma會導致前端和後端之間的溝通不暢(或者這是一個錯誤的假設?),而後端抱怨它。
凌晨,這是一個有趣的!
我同意prgama的東西可能是一個bug,但我很確定&T :: Foo沒有錯。好吧,它什麼都不做,但是語法是有效的,這裏的目的是爲了解決問題。實際的代碼是std :: bind(&T :: Foo,instanceOfT),它又是完全有效的,並且產生了一個可調用的函數對象。沒有編譯器錯誤。 – stijn 2012-03-22 09:28:55
@stijn哈,你在你的真實代碼中完成了我的建議,使用了std :: bind。這將有助於瞭解這一點。在這種情況下,爲了解決這個問題,我會向微軟提交一份錯誤報告。他們修復它的速度非常快。例如,相關修復:http://connect.microsoft.com/VisualStudio/feedback/details/122489/warning-c4793-and-pragma-unmanaged。我會修改我的答案。 – 2012-03-22 17:13:33
+1這個意外複雜問題的優秀答案。 – ComicSansMS 2012-03-22 18:45:23