這篇文章說明自 Visual Studio 2015 回溯到 Visual Studio 2003 的所有的重大變更,並會在文章中使用「新行為」或 「目前」表示 Visual Studio 2015 及更新版本。 「舊行為」和「之前」等詞彙指的是 Visual Studio 2013 和較早的版本。
如需最新 Visual Studio 版本的相關資訊,請參閱 Visual Studio 中 C++ 的最新動向及 Visual Studio 中的 C++ 一致性改善。
注意
Visual Studio 2015 與 Visual Studio 2017 之間沒有二進位相容性破壞的變更。
當您升級到新版 Visual Studio 時,先前正常編譯及執行的程式碼可能會發生編譯和 (或) 執行階段錯誤。 新版本中造成這類問題的變更稱為「重大變更」,通常是因為 C++ 語言標準、函式簽章或記憶體內部物件配置的修改所需的重大調整。
為了避免發生難以偵測及診斷的執行階段錯誤,我們建議您絕不要以靜態方式連結至使用不同版本編譯器所編譯的二進位檔。 此外,當您升級 EXE 或 DLL 專案時,請務必也要升級它所連結的程式庫。 請勿在使用不同版本編譯器所編譯的二進位檔 (包括 DLL) 之間,傳遞 CRT (C 執行階段) 或 C++ 標準程式庫 (C++ 標準程式庫) 類型。 如需詳細資訊,請參閱 在 DLL 邊界傳遞 CRT 物件的潛在錯誤 (Potential Errors Passing CRT Objects Across DLL Boundaries)。
您絶對不要為不是 COM 介面或 POD 物件之物件撰寫依賴特定配置的程式碼。 如果您撰寫了這種程式碼,則必須確定它在升級之後可以正確運作。 如需詳細資訊,請參閱 ABI 界限上的可攜性。
此外,隨著編譯器合規性不斷改進,有時候可能會改變編譯器解讀您現有原始程式碼的方式。 例如,可能會在您建置時發現不同或新的錯誤,甚至程式碼的行為與上版組建不同且看似正常運作。 雖然這些改善並非本文件中所討論的中斷性變更,但為了解決這些問題,仍然可能需要變更原始程式碼:
Visual Studio 2015 一致性變更
C 執行階段程式庫 (CRT)
一般變更
重構的二進位檔
CRT 程式庫已重構成兩個不同的二進位檔:通用 CRT (ucrtbase),其中包含大部分的標準功能,以及 VC 執行階段程式庫 (vcruntime)。 vcruntime 程式庫包含編譯器相關功能,例如例外狀況處理和內建。 如果您使用的是預設專案設定,則此變更不會對您造成影響,因為連結器會自動使用新的預設程式庫。 如果您已將此專案 [連結器] 屬性的 [忽略所有預設程式庫] 設定為 [是],或您在命令列使用
/NODEFAULTLIB連結器選項,則必須更新程式庫清單 (在 [其他相依性] 屬性中),藉此包含重構的新程式庫。 以對等的重構程式庫取代舊的 CRT 程式庫 (libcmt.lib、libcmtd.lib、msvcrt.lib、msvcrtd.lib)。 這兩個重構程式庫中的任何一個皆有靜態 (.lib) 和動態 (.dll) 版本,也都有發行 (沒有後置詞) 和偵錯版本 (具有 "d" 後置詞)。 此動態版本具有您可與其連結的匯入程式庫。 這兩個重構的程式庫為通用的 CRT (具體而言,即 ucrtbase.dll 或 ucrtbase.lib、ucrtbased.dll 或 ucrtbased.lib) 和 VC 執行階段程式庫 (即libvcruntime.lib、vcruntimeversion.dll、libvcruntimed.lib及 vcruntimedversion.dll)。 Visual Studio 2015 和 Visual Studio 2017 中的 version 皆為 140。 請參閱 CRT Library Features。
<locale.h>
localeconv若在 locale.h 中宣告的
localeconv函式啟用每執行緒區域設置,它現在能正確運作。 在此程式庫的舊版中,這個函式會傳回全域地區設定的lconv資料,而非此執行緒的地區設定。如果您使用個別執行緒的地區設定,您應該檢查您的
localeconv使用方式。 如果您的程式碼假設所傳回lconv資料是全域地區設定,您應該加以更正。
<math.h>
C++ 的數學函式庫多載
在舊版中,
<math.h>已針對部分(但不是全部)的數學程序庫函式定義了 C++ 多載。 其餘的多載均位於<cmath>標頭中。 只包含<math.h>的程式碼可能會出現函式多載解析的問題。 現在,C++ 多載皆已從<math.h>中移除,而且只能在<cmath>中找到。若要解析錯誤,請包含
<cmath>,以取得從<math.h>中移除的函式宣告。 以下功能已移動:-
double abs(double)和float abs(float) -
double pow(double, int)、float pow(float, float)、float pow(float, int)、long double pow(long double, long double)、long double pow(long double, int) -
float和long double版本的浮動點函式acos、acosh、asin、asinh、atan、atanh、atan2、cbrt、ceil、copysign、cos、cosh、erf、erfc、exp、exp2、expm1、fabs、fdim、floor、fma、fmax、fmin、fmod、frexp、hypot、ilogb、ldexp、lgamma、llrint、llround、log、log10、log1p、log2、lrint、lround、modf、nearbyint、nextafter、nexttoward、remainder、remquo、rint、round、scalbln、scalbn、sin、sinh、sqrt、tan、tanh、tgamma及trunc
如果您的程式碼使用
abs搭配浮點數類型,且只包含<math.h>標頭,則浮點版本將不再可用。 即使使用浮點引數,現在仍會將呼叫解析成abs(int),這會產生錯誤:warning C4244: 'argument' : conversion from 'float' to 'int', possible loss of data若要修正此警告,可將
abs呼叫取代為abs的浮點數版本;例如fabs(適用於雙精度浮點引數) 或fabsf(適用於浮點引數),或包含<cmath>標頭,以繼續使用abs。-
浮點的一致性
針對特殊案例的輸入,例如 NaN 和無限大,為了與 IEEE-754 和 C11 附錄 F 規格更加一致,此數學程式庫已有許多變更。 例如,以往在舊版的程式庫中通常將靜默 NaN 輸入視為錯誤,但現在不再如此。 請參閱 IEEE 754 標準 和 C11 標準的附錄 F。
這些變更不會造成編譯時期錯誤,但根據此標準,可能會導致程式的行為不同且更加正確。
FLT_ROUNDS
在 Visual Studio 2013 中,FLT_ROUNDS 巨集會擴展為常數運算式,但這是不正確的,因為在執行階段捨入模式是可透過調用 fesetround 進行配置的。 FLT_ROUNDS 巨集現在具有動態性,並能正確反映當前的捨入模式。
<new> 和 <new.h>
new和delete在此程式庫的舊版中,實作定義的 operator new 和 delete 函式會從此執行階段程式庫 DLL (例如,msvcr120.dll) 中匯出。 這些 operator 函式現在一律以靜態方式連結到您的二進位檔,即使是使用執行階段程式庫 DLL 亦同。
對於原生或混合程式碼 (
/clr) 而言,這不是中斷性變更,但對於編譯成 /clr:pure 的程式碼,此變更可能會造成您的程式碼無法編譯。 若將程式碼編譯成/clr:pure,可能必須新增#include <new>或#include <new.h>來因應此變更所造成的建置錯誤。/clr:pure選項在 Visual Studio 2015 中已淘汰,且 Visual Studio 2017 已不支援此選項。 必須是「純」程式碼才能移植到 C#。
<process.h>
_beginthread和_beginthreadex_beginthread和_beginthreadex函式現在會保留模組的參考,在該模組中,執行緒程序是為了該執行緒持續期間而定義。 這有助於確保在執行緒執行到完成之前,不會卸載模組。
<stdarg.h>
va_start和參考型別編譯 C++ 程式碼時,現在
va_start會在編譯時期驗證傳遞給它的引數不屬於參考型別。 此 C++ 標準禁止參考類型引數。
<stdio.h> 和 <conio.h>
printf 和 scanf 系列的函式現在已定義為內嵌。
所有
printf與scanf函式的定義均已內嵌移至<stdio.h>、<conio.h>及其他 CRT 標頭內。 對於在本地宣告這些函式但未包含適當的 CRT 標頭檔的任何程式而言,這項重大變更會導致連結器錯誤 (LNK2019,無法解析的外部符號)。 如有可能,您應該更新程式碼來包含 CRT 標頭 (亦即,新增#include <stdio.h>) 及內嵌函式,但若您不想修改程式碼來包含這些標頭檔案,替代解決方案是將legacy_stdio_definitions.lib新增至您的連結器輸入。若要在 IDE 中將此程式庫新增至您的連結器輸入,請開啟專案節點的操作功能表,選擇 [屬性] ,然後在 [專案屬性] 對話方塊中選擇 [連結器] ,並編輯 [連結器輸入],將
legacy_stdio_definitions.lib新增至分號分隔的清單。如果您的專案與靜態程式庫連結,且此程式庫使用 2015 之前的 Visual Studio 版本進行編譯,則此連結器可能會回報無法解析的外部符號。 這些錯誤可能會參考
_iob、_iob_func的內部定義,或某些<stdio.h>函式以 imp* 形式的相關匯入。 Microsoft 建議您在升級專案時,應以最新版本的 C++ 編譯器和程式庫重新編譯所有的靜態程式庫。 如果該程式庫是無法取得其來源的第三方程式庫,您可以選擇向第三方要求更新的二進位檔,或者將您對該程式庫的使用封裝成使用舊版本編譯器和程式庫編譯的獨立 DLL。警告
如果您要以 Windows SDK 8.1 或更早版本連結,您可能會遇到這些無法解析的外部符號錯誤。 在該情況下,您應該如先前所述地將 legacy_stdio_definitions.lib 加入連結器輸入來解決此錯誤。
若要針對無法解析的符號錯誤進行疑難排解,您可以嘗試使用
dumpbin.exe來檢查二進位檔中定義的符號。 請嘗試下列的命令列,檢視程式庫中定義的符號。dumpbin.exe /LINKERMEMBER somelibrary.libgets 和 _getws
已移除 gets 和 _getws 函式。 因為無法安全使用,所以 gets 函式已從 C11 中的 C 標準程式庫中移除。 _getws 函式曾是 Microsoft 擴充功能,相當於 gets,但卻是用於寬字串。 您可以考慮使用這些函式的替代函式 fgets、fgetws、gets_s 及 _getws_s。
_cgets 和 _cgetws
已移除 _cgets 和 _cgetws 函式。 您可以考慮使用這些函式的替代函數 _cgets_s 與 _cgetws_s。
無限大和 NaN 格式化
在舊版本中,會使用一組 MSVC 特定的 Sentinel 字串,將無限大和 NaN 格式化。
無限大:1.#INF
無訊息的 NaN:1.#QNAN
訊號 NaN:1.#SNAN
不確定的 NaN:1.#IND
其中任何一種格式都可能會有符號,並且根據欄位寬度和精確度而可能稍有不同 (有時會出現不尋常的效果,例如:
printf("%.2f\n", INFINITY)可能會顯示為 1.#J,因為 #INF 會被「四捨五入」精確至小數點後兩位數)。 C99 引入了關於如何格式化無限值和非數值(NaN)的新要求。 MSVC 實作現在符合這些需求。 新的字串如下:無限大:inf
安靜的 NaN:nan
訊號 NaN:nan(snan)
不確定的 NaN:nan(ind)
其中任何一項都可以以符號作為前綴。 如果使用了大寫格式指定名稱 (%F,而非 %f),則此字串會視需要以大寫字母 (
INF,而非inf) 來列印。scanf 函式在經過修改之後,已可剖析這些新字串,讓這些字串現在可以透過
printf及scanf來回。浮點數格式化和解析
已引進新的浮點數格式化和剖析演算法來改善正確性。 此變更會影響 printf 和 scanf 系列函式及 strtod 之類的函式。
舊格式的演算法會產生有限位數的數字,然後以零填滿剩餘的小數位數。 它們通常會產生對原始浮點數值反覆存取的字串,但如果您想要精確值 (或其中最接近的十進位表示),就會不夠合適。 新的格式演算法會視需要產生足夠多的位數,用來代表該值 (或用來填入指定的精確度)。 作為改進的例子,考慮列印 2 的高次冪時的結果:
printf("%.0f\n", pow(2.0, 80))舊的輸出:
1208925819614629200000000新的輸出:
1208925819614629174706176舊的剖析演算法只會考慮輸入字串中最多 17 個有效位數,並會捨棄其餘位數。 這種方法足以生成與字串所表示的值非常接近的近似值,且結果通常會非常接近正確捨入的結果。 新的實作會考慮所有出現的數字,並針對所有輸入(長度最多 768 位數)產生正確四捨五入的結果。 此外,這些函式現在會遵循捨入模式 (可透過 fesetround 控制)。 這可能是一個潛在的破壞性行為變更,因為這些函式可能會輸出不同的結果。 新的結果永遠比舊的結果正確。
十六進位和無限大/NaN 浮點數剖析
如同以上所述,浮點數剖析演算法現在會剖析十六進位浮點數字串 (例如那些由 %a 和 %A printf 格式指定名稱所產生的浮點數字串) 和所有由此
printf函式產生的無限大和 NaN 字串。%A 和 %a 零填補
%a 和 %A 格式規範會將浮點數格式化為十六進位的尾數和二進位的指數。 在先前的版本中,
printf函式無法正確對字串進行零填補。 例如,printf("%07.0a\n", 1.0)會印出 00x1p+0,但這應為 0x01p+0。 這項缺陷已獲得修正。%A 和 %a 的精確度
在此程式庫的舊版中,%A 和 %a 格式規範的預設精確度是 6。 為了與 C 標準一致,現在此預設精確度為 13。
這是在使用包含 %A 或 %a 的格式字串的任何函式中,輸出結果的執行時行為變更。 在舊版的行為中,使用 %A 規範的輸出可能為 "1.1A2B3Cp+111"。 現在對於相同值的輸出是 "1.1A2B3C4D5E6F7p+111"。 若要恢復舊的行為,您可以指定精度,例如 %.6A。 請參閱精確度規格。
%F 格式化字元
現在支援 %F 格式/轉換規範。 它在功能上等同於 %f 格式規範符,只是無限值和 NaN 會使用大寫字母格式。
在舊版中,此實作用來剖析 F 和 N 為長度修飾詞。 這種行為可追溯至分段位址空間的年代:這些長度修飾詞曾分別用來表示遠指標和近指標,如同 %Fp 或 %Ns。 已移除這種行為。 如果遇到 %F,現在會將它視為 %F 格式指定名稱;如果遇到 %N 時,現在會將它視為無效的參數。
指數格式化
%e 和 %E 格式規範會將浮點數格式化為十進位的尾數和指數。 在某些情況下,%g 和 %G 格式規範也會將數字格式化為這種形式。 在舊版中,CRT 永遠會產生具有三位數指數的字串。 例如,
printf("%e\n", 1.0)會印出 1.000000e+000,這不是正確的。 如果可使用一位數或兩位數表示該指數,則 C 要求只能印出兩位數。Visual Studio 2005 加入了全域一致性開關:_set_output_format。 程式可以用引數 _TWO_DIGIT_EXPONENT 呼叫這個函式,以啟用符合標準的指數列印。 這項預設行為已變更為符合標準的指數列印模式。
格式字串驗證
在舊版中,
printf和scanf函式會以無訊息模式接受許多無效的格式字串,有時會產生不尋常的作用。 例如,會將 %hlhlhld 視為 %d。 現在會將所有無效的格式字串視為無效的參數。fopen 模式字串驗證
在舊版中,
fopen系列的函式會以無訊息模式接受某些無效的模式字串 (例如r+b+)。 現在會偵測到無效的模式字串並將其視為無效的參數。_O_U8TEXT模式
_setmode 函式現在已可正確回報 in_O_U8TEXT 模式中開啟的資料流模式。 在此程式庫的舊版中,它會報告這類資料流為在 _O_WTEXT 中所開啟。
如果您的程式碼解譯 _O_WTEXT 模式,其中資料流的編碼是 UTF-8,這就會是一項重大變更。 如果您的應用程式不支援 UTF_8,請考慮將這項日漸普遍之編碼方式的支援加入。
snprintf 和 vsnprintf
現在已實作 snprintf 和 vsnprintf。 舊版程式碼通常會為巨集版本的這些函式提供定義,因為 CRT 程式庫並沒有實作它們,但是在較新版本中已不再需要這些函式。 現在,若在包含
<stdio.h>之前將 snprintf 或 vsnprintf 定義為巨集,編譯會出錯,並顯示錯誤訊息指出巨集的定義位置。一般來說,修正這個問題之方式為刪除使用者程式碼中
snprintf或vsnprintf的任何宣告。tmpnam 產生可使用的檔案名稱
在舊版中,
tmpnam和tmpnam_s函式會在磁碟機的根目錄中 (例如 \sd3c.) 產生檔案名稱。 現在這些函式會在暫存目錄中產生可用的檔案名稱路徑。FILE 封裝
在舊版中,已於
<stdio.h>中公開定義完整的 FILE 類型,如此一來,使用者程式碼就能進入 FILE 並修改其內部項目。 此程式庫已變更為隱藏實作詳細資料。 作為此變更的一部分,<stdio.h>中定義的 FILE 現在是不透明類型,且無法從 CRT 本身外部存取其成員。_outp 和 _inp
<stdlib.h>、<malloc.h> 和 <sys/stat.h>
strtof 和 wcstof
當
strtof和wcstof函式的值無法以浮點數表示時,就無法將errno設定為 ERANGE。 這個錯誤是這兩個函式所特定;strtod、wcstod、strtold和wcstold函式不會受到影響。 這個問題已獲得修正,且是執行階段的中斷性變更。對齊的配置函式
在先前版本中,對齊配置函式 (
_aligned_malloc、_aligned_offset_malloc等) 會以無訊息的方式接受對齊等於 0 的區塊要求。 要求的對齊必須是 2 的次方,而 0 並不符合這一條件。 現在會將要求的對齊為 0 視為無效參數。 這個問題已獲得修正,且是執行階段的中斷性變更。堆積函式
已移除
_heapadd、_heapset和_heapused函式。 這些函式已無作用,因為 CRT 已更新為使用 Windows 堆積。smallheap
已移除
smallheap連結選項。 請參閱 Link Options。_統計
_stat系列函式會在 Visual Studio 2015 中使用CreateFile,而不是如 Visual Studio 2013 和更早版本中的FindFirstFile。 這表示,如果路徑參考的是目錄,則以斜線結尾的路徑上的_stat會成功,這與之前函式會發生錯誤並將errno設定為ENOENT的情況不同。
<string.h>
wcstokwcstok函式的簽章已變更為符合 C 標準所需的簽章。 在此程式庫的舊版中,這個函式的簽章是:wchar_t* wcstok(wchar_t*, wchar_t const*)它使用內部個別執行緒內容來追蹤呼叫之間的狀態,如同
strtok所執行的。 此函式現在已有wchar_t* wcstok(wchar_t*, wchar_t const*, wchar_t**)簽章,需要呼叫者使用第三個引數將內容傳遞給此函式。已將舊簽章新增至新的
_wcstok函式,藉此簡化移轉。 在編譯 C++ 程式碼時,還有一個具有舊簽章的wcstok內嵌多載。 會將這個多載宣告為已棄用。 在 C 程式碼中,您可能會定義 _CRT_NON_CONFORMING_WCSTOK,使用_wcstok來取代wcstok。
<time.h>
時鐘
在舊版中,使用 Windows API
GetSystemTimeAsFileTime實作clock函式。 連同這項實作,clock 函式會受系統時間影響,因此並不一定是單調函式。 clock 函式已根據QueryPerformanceCounter重新實作且現在為單調函式。fstat 和 _utime
在舊版中,
_stat、fstat和_utime函式並未正確處理日光節約時間。 在 Visual Studio 2013 之前,這些函式全都不正確地調整標準時間,如同它們是在夏令時間一樣。在 Visual Studio 2013 中,已在
_stat系列函式中修正此問題,但fstat和_utime系列函式中的類似問題並未獲修正。 這項部分修正會導致因函式之間不一致而產生的問題。 現在已修正fstat和_utime系列函式,因此,這些函式現在全都能正確且一致地處理日光節約時間。asctime
在舊版中,
asctime函式會在個位數的天數前面填補零,例如:Fri Jun 06 08:00:00 2014。 此規格需要這類天數以空格填補,如Fri Jun 6 08:00:00 2014中所示。 已修正此問題。strftime 和 wcsftime
strftime和wcsftime函式現已支援 %C、%D、%e、%F、%g、%G、%h、%n、%r、%R、%t、%T、%u 和 %V 格式規範。 此外,E 和 O 修飾詞會被剖析,但是將會忽略。會指定 %c 格式規範為在目前地區設定下產生「適當的日期和時間表示法」。 在 C 的地區設定中,這個表示法必須與
%a %b %e %T %Y相同,即與asctime所產生的形式相同。 在舊版中,%c 格式規範符未能正確地以MM/DD/YY HH:MM:SS的表示法對時間進行格式化。 已修正此問題。timespec 和 TIME_UTC
<time.h>標頭現在會定義 C11 標準的timespec類型與timespec_get函式。 此外也定義了 TIME_UTC 巨集,可與timespec_get函式搭配使用。 這次更新屬於「破壞性的更動」,對那些識別碼定義衝突的程式碼將產生影響。CLOCKS_PER_SEC
現在 CLOCKS_PER_SEC 巨集會展開成類型
clock_t的整數,這是 C 語言的要求。
C++ 標準程式庫
為了啟用新的最佳化和偵錯檢查,Visual Studio 所實作的 C++ 標準程式庫是刻意中斷各個版本之間的二進位碼相容性 (Binary Compatibility)。 因此,使用 C++ 標準程式庫時,使用不同版本所編譯的目的檔和靜態程式庫不可以混合在一個二進位檔 (EXE 或 DLL) 中,也不可以在使用不同版本所編譯的二進位檔之間傳遞 C++ 標準程式庫物件。 這種混合會引發有關 _MSC_VER 不符的連結器錯誤。 (_MSC_VER 是包含此編譯器主要版本的巨集,例如對於 Visual Studio 2013 為 1800。)這項檢查無法偵測 DLL 混合,且無法偵測包含 Visual Studio 2008 及較舊版本的混合。
C++ 標準程式庫引入檔案
C++ 標準程式庫標頭中的 Include 結構已有一些變更。 C++ 標準程式庫標頭可以以未指定的方式互相包含。 一般來說,您在撰寫程式碼時,應依據 C++ 標準小心地包含程式碼所需的所有標頭,而不是依賴哪些 C++ 標準程式庫標頭會自動包含其他標頭。 這使程式碼具有跨版本和跨平台的可移植性。 在 Visual Studio 2015 中,至少有兩種標頭的變更會影響使用者程式碼。 首先,
<string>不再包含<iterator>。 其次,<tuple>現在會宣告std::array,而不需包含所有<array>,這可能會透過下列程式碼建構組合來中斷程式碼:您的程式碼具有名為 "array" 的變數及 using 指示詞 "using namespace std;",而您會包含內含<functional>(現在會宣告<tuple>) 的 C++ 標準程式庫標頭 (例如std::array)。steady_clock
<chrono>的steady_clock實作已變更,以滿足 C++ 標準的穩定性和單調性保持要求。steady_clock現在會以QueryPerformanceCounter為基礎,而high_resolution_clock現在是steady_clock的 typedef。 因此,在 Visual Studio 中,steady_clock::time_point現在是chrono::time_point<steady_clock>的 typedef;但是,其他實作的情況不一定也如此。配置器和常數
我們現在要求分配器的等式/不等式比較在兩邊接受常數參數。 如果您的分配器如下定義這些運算子,
bool operator==(const MyAlloc& other)然後,您應該更新它們,並將它們宣告為「const」成員:
bool operator==(const MyAlloc& other) const常數元素
C++ 標準一律禁止 const 元素 (例如
vector<const T>或set<const T>) 的容器。 Visual Studio 2013 及較舊版接受這類容器。 在目前版本中,這類容器無法編譯。std::allocator::deallocate
在 Visual Studio 2013 和舊版中,
std::allocator::deallocate(p, n)會忽略針對 n 而傳入的引數。 C++ 標準一律要求 n 必須等於作為第一個引數傳遞至allocate引動過程的值,其會傳回 p。 不過,在目前版本中,會檢查 n 的值。 不符合標準要求的 n 引數傳遞程式碼,可能會在執行時崩潰。hash_map 和 hash_set
非標準的標頭檔
<hash_map>和<hash_set>在 Visual Studio 2015 中已淘汰,並將於後續版本中移除。 請改用<unordered_map>和<unordered_set>。比較子和 operator()
關聯式容器 (
<map>家族) 現在要求其比較器必須具有常量可調用的函數調用運算子。 現在比較子類別宣告中的下列程式碼無法編譯:bool operator()(const X& a, const X& b)若要解決這個錯誤,請將此函式宣告變更為:
bool operator()(const X& a, const X& b) const類型特性
已移除來自較早版本 C++ 標準草案之類型特性的舊名稱。 這些在 C++11 中已有所變更,且已在 Visual Studio 2015 中更新為 C++11 的值。 下表顯示舊名稱和新名稱。
舊名稱 新名稱 新增參考 加入左值參考 (add_lvalue_reference) 具有預設建構子 是否可預設建構 具有複製建構子 is_copy_constructible (是否可複製建構) 具有移動建構函數 可移動構造性 has_nothrow_constructor (無拋出例外的建構函式) 具有不拋出異常的默認構造功能 非拋出異常的預設建構子 具有不拋出異常的默認構造功能 has_nothrow_copy (具有無拋出例外的複製功能) is_nothrow_copy_constructible (不拋出例外的複製構造性) 具有不引發異常的複製建構函數 is_nothrow_copy_constructible (不拋出例外的複製構造性) 擁有無拋出異常的移動構造函數 is_nothrow_move_constructible(無拋出異常的移動建構可能性) 具有不拋出異常賦值屬性 拷貝賦值操作是否不會拋出異常 擁有無拋異常賦值功能 拷貝賦值操作是否不會拋出異常 支援無拋出例外的移動指定 is_nothrow_move_assignable(檢查型別是否有不拋出例外的移動指派運算子) 具有簡單構造函數 是否顯而易見可默認構造 擁有簡單預設構造函數 是否顯而易見可默認構造 標記_簡單拷貝 is_trivially_copy_constructible(是否為可簡單複製構造的) has_trivial_move_constructor(具平凡移動構造函數) 易於實現移動構造的类型 基本賦值檢查 is_trivially_copy_assignable(判斷型別是否能被簡單地進行複製指定) 有簡單移動賦值 is_trivially_move_assignable (是否可以使用簡單移動指派) 具有簡單析構函數 具自動銷毀特性 launch::any 和 launch::sync 原則
已移除非標準的
launch::any和launch::sync策略。 針對launch::any,請改為使用launch:async | launch:deferred。 對於launch::sync,請使用launch::deferred。 請參閱 launch Enumeration。
MFC 和 ATL
Microsoft基礎課程 (MFC)
因為其大小太大而不再隨附於 Visual Studio 的「一般」安裝。 若要安裝 MFC,請在 Visual Studio 2015 安裝程式中選擇 [自訂] 安裝選項。 如果已安裝 Visual Studio 2015,您可以再次執行 Visual Studio 安裝程式來安裝 MFC。 選擇 [自訂] 安裝選項,然後選擇 [Microsoft Foundation Classes]。 您可以從 [控制台] 控制項、[程式和功能],或從安裝媒體執行 Visual Studio 安裝程式。
Visual C++ 可轉散發套件仍然包含這個程式庫。
並行執行階段
Windows.h 中的 Yield 巨集指令與 concurrency::Context::Yield 發生衝突
並行執行階段之前使用
#undef來取消定義 Yield 巨集,以避免 Windows.h 中定義的 Yield 巨集與concurrency::Context::Yield函式發生衝突。 此#undef已移除,並新增了不衝突的對等 API 呼叫 concurrency::Context::YieldExecution。 若要因應與 Yield 的衝突,您可以更新程式碼,改成呼叫YieldExecution函式,或在呼叫位置將Yield函式名稱加上括弧,如下列範例所示:(concurrency::Context::Yield)();
Visual Studio 2015 的編譯器一致性改進
從舊版升級程式碼時,也可能會因為 Visual Studio 2015 的一致性改進而發生編譯器錯誤。 這些改進並不影響較舊版 Visual Studio 的二進位相容性,但可能會產生之前從未發生過的編譯器錯誤。 如需詳細資訊,請參閱Visual C++ What's New 2003 through 2015 (Visual C++ 2003 至 2015 的新功能)。
在 Visual Studio 2015 中,隨著編譯器一致性不斷改進,有時候可能會改變編譯器解讀您現有原始程式碼的方式。 因此,您進行建置時可能會發生新的或不同的錯誤,甚至程式碼的行為出現與先前正常建置和運行時不同的差異。
幸運的是,這些差異對大部分的程式碼只有一點影響,或沒有任何影響。 需要原始程式碼或其他變更才能解決這些差異時,修正會傾向小型且簡單易懂。 我們已經包含了許多先前被接受但現在可能需要更改的原始程式碼範例(之前),以及用於更正它們的修正(之後)。
雖然這些差異可能影響您的原始程式碼或其他組建成品,但並不影響每版 Visual Studio 更新之間的二進位相容性。 所謂的「重大變更」會更嚴重,可能影響到二進位相容性。然而,這類的二進位相容性破壞僅會在 Visual Studio 的主要版本之間發生,例如,Visual Studio 2013 到 Visual Studio 2015。 如需了解發生於 Visual Studio 2013 與 Visual Studio 2015 之間具破壞性的變更資訊,請參閱 Visual Studio 2015 一致性變更。
Visual Studio 2015 的一致性改善
/Zc:forScope- 選項
編譯器選項
/Zc:forScope-已棄用,並將在未來版本中移除。Command line warning D9035: option 'Zc:forScope-' has been deprecated and will be removed in a future release通常,這個選項是在非標準程式碼的迴圈變數應該已經超出範圍 (根據該標準) 之後,用來允許此非標準程式碼。 只有當您使用
/Za選項編譯時才需要這樣做,因為在沒有指定/Za的情況下,會一律允許在迴圈結束之後使用 for 迴圈變數。 如果您不在意標準一致性 (例如,如果您並不打算將程式碼設計成可移植到其他編譯器),則可以關閉/Za選項 (或將停用語言延伸模組屬性設為否)。 如果您在意撰寫可攜式且符合標準規範的程式碼,就應該重寫程式碼,藉由將這類變數的宣告移至該迴圈外,使其符合此標準。// C2065 expected int main() { // Uncomment the following line to resolve. // int i; for (int i = 0; i < 1; i++); i = 20; // i has already gone out of scope under /Za }/Zg編譯器選項/Zg編譯器選項 (產生函式原型) 不再提供使用。 這個編譯器選項之前已遭取代。您再也無法從命令列藉由 mstest.exe 使用 C++ /CLI 來執行單元測試。 請改用 vstest.console.exe。 請參閱 VSTest.Console.exe 命令列選項。
可變動的關鍵字
在以前可以順利編譯的地方,
mutable儲存類別規範現在已不再被允許。 現在,此編譯器會發生錯誤 C2071 (不合法的儲存類別)。 根據該標準,mutable指定名稱只能套用至類別資料成員的名稱,而不能套用至宣告為 const 或 static 的名稱,也不能套用至參考成員。例如,請考慮下列程式碼:
struct S { mutable int &r; };舊版的編譯器可接受這種做法,但現在該編譯器會產生下列錯誤:
error C2071: 'S::r': illegal storage class若要修正此錯誤,移除冗餘的
mutable關鍵字。char_16_t 與 char32_t
您無法再使用
char16_t或char32_t作為typedef中的別名,因為現在會將這些類型視為內建類型。 使用者和程式庫作者通常會分別定義char16_t和char32_t作為uint16_t和uint32_t的別名。#include <cstdint> typedef uint16_t char16_t; //C2628 typedef uint32_t char32_t; //C2628 int main(int argc, char* argv[]) { uint16_t x = 1; uint32_t y = 2; char16_t a = x; char32_t b = y; return 0; }若要更新程式碼,請移除
typedef宣告,並重新命名任何其他與這些名稱相衝突的識別碼。非類型範本參數
當您提供明確的樣板引數時,現今與非類型樣板參數相關的特定程式碼進行的類型相容性檢查是正確的。 例如,下列程式碼可在舊版的 Visual Studio 中正確無誤地編譯。
struct S1 { void f(int); void f(int, int); }; struct S2 { template <class C, void (C::*Function)(int) const> void f() {} }; void f() { S2 s2; s2.f<S1, &S1::f>(); }在當前的編譯器中,正確地會產生錯誤,因為模板參數類型與模板實參不匹配(該參數是指向常數成員的指標,但函式 f 是一個非常數函式):
error C2893: Failed to specialize function template 'void S2::f(void)'note: With the following template arguments:note: 'C=S1'note: 'Function=S1::f'若要解決程式碼中的這個錯誤,請確定您使用的樣板引數的類型與此樣板參數的宣告類型相符。
__declspec(align)該編譯器已不再接受函式上的
__declspec(align)。 之前一律會忽略此建構,但現在會產生編譯器錯誤。error C3323: 'alignas' and '__declspec(align)' are not allowed on function declarations若要修正這個問題,請從此函式宣告中移除
__declspec(align)。 因為它沒有任何作用,移除它並不會變更任何項目。例外狀況處理
有幾項與例外處理相關的變更。 首先,例外狀況物件必須可複製或可移動。 下列程式碼在 Visual Studio 2013 中可成功編譯,但無法在 Visual Studio 2015 中編譯:
struct S { public: S(); private: S(const S &); }; int main() { throw S(); // error }問題在於複製建構函式是私有的,因此在處理例外狀況的過程中,物件無法被複製。 相同情況也發生在宣告複製建構函式為
explicit的時候。struct S { S(); explicit S(const S &); }; int main() { throw S(); // error }若要更新程式碼,確定例外狀況物件的複製建構函式是
public,而且未標示為explicit。以傳值方式攔截例外狀況也需要可複製的例外狀況物件。 下列程式碼在 Visual Studio 2013 中可成功編譯,但無法在 Visual Studio 2015 中編譯:
struct B { public: B(); private: B(const B &); }; struct D : public B {}; int main() { try { } catch (D d) // error { } }您可以藉由變更
catch的參數類型為參考來修正此問題。catch (D& d) { }後接巨集的字串常量
該編譯器現在支援使用者定義的常值。 因此,後接巨集且不含任何中間空白的字串常值會被解釋為使用者定義常值,這可能會導致錯誤或非預期的結果。 例如,在舊版編譯器中,下列程式碼可順利編譯:
#define _x "there" char* func() { return "hello"_x; } int main() { char * p = func(); return 0; }該編譯器將此程式碼解釋為字串字面量 "hello",接下來展開為字串 "there" 的巨集,然後將這兩個字串字面量串接成一個。 在 Visual Studio 2015 中,編譯器會將此序列解譯為使用者定義的常值,但是沒有相符的使用者定義常值
_x,所以會產生錯誤。error C3688: invalid literal suffix '_x'; literal operator or literal operator template 'operator ""_x' not found note: Did you forget a space between the string literal and the prefix of the following string literal?若要修正這個問題,請在字串常值和巨集之間加入空格。
相鄰字串常值
與先前類似,因為字串剖析方式的變更,在舊版 Visual C++ 中,沒有任何空白的相鄰字串常值(不論是寬字元字串常值或窄字元字串常值)被解讀為單一串接字元串。 在 Visual Studio 2015 中,您必須在兩個字串之間新增空白字元。 例如,下列程式碼必須有所變更:
char * str = "abc""def";若要修正此問題,請在兩個字串之間新增空格:
char * str = "abc" "def";配置 new 與 delete
為了讓
delete運算子與 C++ 14 標準一致,已對它進行變更。 如需此標準之變更的詳細資訊,請參閱 C++ 調整大小的解除配置。 變更當中新增了一種全域delete運算子,該運算子會使用尺寸參數。 中斷性變更是,若您先前使用具有相同簽章的運算子delete(對應至 placement new 運算子),您將會收到編譯器錯誤 (C2956,其發生在使用 placement new 的位置,因為這是程式碼中編譯器嘗試識別適當且相符之delete運算子的位置)。函式
void operator delete(void *, size_t)曾是 placement delete 運算子,對應至 C++ 11 中的 placement new 函式void * operator new(size_t, size_t)。 搭配 C++14 依大小調整的解除配置,此 delete 函式現在為「一般解除配置函式」 (全域delete運算子)。 標準的要求是,如果 placement new 的使用會查詢對應的 delete 函式,並找到一般解除配置函式,則此程式語式錯誤。例如,假設您的程式碼同時定義 placement new 和 placement delete:
void * operator new(std::size_t, std::size_t); void operator delete(void*, std::size_t) noexcept;您已定義的 placement delete 運算子,因與全球大小調整後的新
delete運算子的函式簽章相符,而導致此問題發生。 對於任何size_t和 運算子,請考慮是否可以使用delete以外的其他類型。size_ttypedef的類型取決於編譯器;在 MSVC 中,它是unsigned int的typedef。 較佳的解決方案是使用這類列舉類型:enum class my_type : size_t {};然後,變更 placement new 和
delete的定義,以使用此類型取代size_t成為第二個引數。 您也需要更新對 placement new 的呼叫,以傳遞新類型 (例如,使用static_cast<my_type>,從整數值進行轉換),並更新new和delete的定義,以轉換回整數類型。 您不需要為此使用enum;具有size_t成員的類別類型也適用。另一個解決方案是,您或許可以完全排除 placement new。 如果您的程式碼使用 placement new 實作記憶體集區 (其中 placement 引數是已配置或刪除的物件大小),則依大小調整的解除配置功能或許很適合取代您自己的自訂記憶體集區程式碼,而您可以去除 placement 函式,只使用您自己的雙引數
delete運算子來取代 placement 函式。如果您不想立即更新您的程式碼,則可以使用編譯器選項
/Zc:sizedDealloc-來還原舊版的行為。 如果您使用此選項,雙引數 delete 函式就不存在,也就不會與您的 placement delete 運算子發生衝突。共用體資料成員
聯集的資料成員無法再具有參考類型。 下列程式碼會在 Visual Studio 2013 中成功編譯,但在 Visual Studio 2015 中產生錯誤。
union U1 { const int i; }; union U2 { int & i; }; union U3 { struct { int & i; }; };前一個程式碼會產生下列錯誤:
test.cpp(67): error C2625: 'U2::i': illegal union member; type 'int &' is reference type test.cpp(70): error C2625: 'U3::i': illegal union member; type 'int &' is reference type若要解決這個問題,請將參考類型變更為指標或值。 將此類型變更為指標需要變更使用此等位欄位的程式碼。 將此程式碼變更為值也可能會變更此等位中儲存的資料,這會影響其他欄位,因為在等位類型中的欄位會共用相同的記憶體。 根據值的大小,可能也會改變聯合的大小。
現在匿名聯合體更符合標準。 舊版的編譯器會為匿名聯合生成明確的建構函式和解構函式。 這些編譯器產生的函式已於 Visual Studio 2015 中刪除。
struct S { S(); }; union { struct { S s; }; } u; // C2280前述程式碼會在 Visual Studio 2015 產生下列錯誤:
error C2280: '<unnamed-type-u>::<unnamed-type-u>(void)': attempting to reference a deleted function note: compiler has generated '<unnamed-type-u>::<unnamed-type-u>' here若要解決這個問題,請提供您自己的建構函式和 (或) 解構函式之定義。
struct S { // Provide a default constructor by adding an empty function body. S() {} }; union { struct { S s; }; } u;具匿名結構的聯合
為了符合標準,聯合中的匿名結構成員的執行階段行為已經有所變更。 在建立這類聯合體(union)時,不再隱含呼叫其中匿名結構成員的建構函式。 此外,當聯合超出範圍時,將不再自動呼叫聯合中的匿名結構成員的解構函式。 請考慮以下程式碼,其中聯合體 U 包含一個匿名結構,該結構中含有一個具名的成員結構 S,並且 S 擁有一個析構函式。
#include <stdio.h> struct S { S() { printf("Creating S\n"); } ~S() { printf("Destroying S\n"); } }; union U { struct { S s; }; U() {} ~U() {} }; void f() { U u; // Destructor implicitly called here. } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }在 Visual Studio 2013 中,會在建立 union 時呼叫 S 的建構函式,而在清除函式 f 的堆疊時,會呼叫 S 的解構函式。 但在 Visual Studio 2015 中,不會呼叫此建構函式和解構函式。 該編譯器會提供有關這項行為變更的警告。
warning C4587: 'U::s': behavior change: constructor is no longer implicitly calledwarning C4588: 'U::s': behavior change: destructor is no longer implicitly called若要還原原始行為,請對此匿名結構命名。 不論編譯器版本為何,非匿名結構的執行階段行為是相同的。
#include <stdio.h> struct S { S() { printf("Creating S.\n"); } ~S() { printf("Destroying S\n"); } }; union U { struct { S s; } namedStruct; U() {} ~U() {} }; void f() { U u; } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }或者,請嘗試將建構函式和解構函式的程式碼移到新的函式中,並從 union 的建構函式和解構函式中呼叫這些新的函式。
#include <stdio.h> struct S { void Create() { printf("Creating S.\n"); } void Destroy() { printf("Destroying S\n"); } }; union U { struct { S s; }; U() { s.Create(); } ~U() { s.Destroy(); } }; void f() { U u; } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }範本解析
已變更樣板的名稱解析。 在 C++ 中,考慮名稱解析的候選時,可能出現這種情況:一或多個被認為是可能匹配的名稱產生無效的範本實例化。 這些無效的具現化通常不會造成編譯器錯誤,也就是稱為 SFINAE (替代失敗不是錯誤) 的原則。
現在,如果 SFINAE 需要此編譯器具現化類別樣板的特製化,則在這個程序期間發生的任何錯誤都會是編譯器錯誤。 在舊版中,此編譯器會忽略這類錯誤。 例如,請考慮下列程式碼:
#include <type_traits> template< typename T> struct S { S() = default; S(const S&); S(S& &); template< typename U, typename = typename std::enable_if< std::is_base_of< T, U> ::value> ::type> S(S< U> & &); }; struct D; void f1() { S< D> s1; S< D> s2(s1); } struct B { }; struct D : public B { }; void f2() { S< D> s1; S< D> s2(s1); }如果您使用目前的編譯器編譯時,會產生下列錯誤:
type_traits(1110): error C2139: 'D': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_base_of' ..\t331.cpp(14): note: see declaration of 'D' ..\t331.cpp(10): note: see reference to class template instantiation 'std::is_base_of<T,U>' being compiled with [ T=D, U=D ]這是因為在第一次調用 is_base_of 類別時,
D尚未定義。在這種情況下,修正是在定義此類別之前不使用這類類型特性。 如果您將
B和D的定義移至此程式碼檔案的開頭,則可解決此錯誤。 如果此定義位於標頭檔中,請檢查標頭檔之 include 陳述式的順序,藉此確定在使用有問題的樣板之前,會先編譯任何類別的定義。複製建構函式
在 Visual Studio 2013 和 Visual Studio 2015 中,如果類別具有使用者定義的移動建構函式,但沒有使用者定義的複製建構函式,則編譯器會為該類別產生複製建構函式。 在 Dev14 中,也會將這個隱含產生的複製建構函式標示為"= delete"。
宣告為 extern "C" 的 main 現在需要傳回類型。
下列程式碼現在會產生 C4430。
extern "C" __cdecl main(){} // C4430若要修正錯誤,請新增傳回類型:
extern "C" int __cdecl main(){} // OK成員初始化程序中不允許使用 typename
下列程式碼現在會產生 C2059:
template<typename T> struct S1 : public T::type { S1() : typename T::type() // C2059 { } }; struct S2 { typedef S2 type; }; S1<S2> s;若要修正錯誤,請從初始設定式中移除
typename:S1() : T::type() // OK ...進行明確特製化時會忽略儲存類別。
在下列程式碼中,會忽略靜態儲存類別規範
template <typename T> void myfunc(T h) { } template<> static void myfunc(double h) // static is ignored { }類別範本內 static_assert 中所使用的常數將一律失敗。
下列程式碼會導致
static_assert一律失敗:template <size_t some_value> struct S1 { static_assert(false, "default not valid"); // always invoked }; //other partial specializations here若要因應此問題,將值包裝於
struct中:template <size_t some_value> struct constant_false { static const bool value = false; }; template <size_t some_value> struct S1 { static_assert(constant_false<some_value>::value, "default not valid"); }; //other partial specializations here針對前置宣告的規則強制執行 (只適用於 C)。
下列程式碼現在會產生 C2065:
struct token_s; typedef int BOOL; typedef int INT; typedef int(*PFNTERM)(PTOKEN, BOOL, INT); // C2065: 'PTOKEN' : undeclared identifier若要解決此問題,請新增適當的前置宣告:
struct token_s; typedef int BOOL; typedef int INT; // forward declarations: typedef struct token_s TOKEN; typedef TOKEN *PTOKEN; typedef int(*PFNTERM)(PTOKEN, BOOL, INT);以更一致的方式強制執行函式指標類型
下列程式碼現在會產生 C2197:
typedef int(*F1)(int); typedef int(*F2)(int, int); void func(F1 f, int v1, int v2) { f(v1, v2); // C2197 }模稜兩可的呼叫多載函式
下列程式碼現在會產生 C266:'N::bind': 模稜兩可的呼叫多載函式
template<typename R, typename T, typename T1, typename A1> void bind(R(T::*)(T1), A1&&); namespace N { template <typename T, typename R, typename ... Tx> void bind(R(T::*)(Tx...), T* ptr); } using namespace N; class Manager { public: void func(bool initializing); void mf() { bind(&Manager::func, this); //C2668 } };若要修正錯誤,您可以完整限定對
bind: N::bind(...)的呼叫。 不過,如果此變更顯然是透過未宣告的識別碼 (C2065) 來進行,則其可能適合使用using宣告來修正此問題。此模式經常發生於
Microsoft::WRL命名空間中的 ComPtr 和其他類型。修正不正確的位址
下列程式碼現在會產生 C2440:'=': 無法從 'type *' 轉換為 'type'。 若要修正錯誤,請將 &(type) 變更為 (type),以及將 (&f()) 變更為 (f())。
// C typedef void (*type)(void); void f(int i, type p); void g(int); void h(void) { f(0, &(type)g); } // C++ typedef void(*type)(void); type f(); void g(type); void h() { g(&f()); }字串字面值是固定陣列
下列程式碼現在會產生 C2664:'void f(void )':無法將引數 1 從 'const char ()[2]' 轉換為 'void *'
void f(void *); void h(void) { f(&__FUNCTION__); void *p = &""; }若要修正錯誤,請將函式參數類型變更為
const void*,或是變更h的內容,使其看起來如以下範例所示:void h(void) { char name[] = __FUNCTION__; f( name); void *p = &""; }C++11 UDL 字串
下列程式碼現在會產生錯誤 C3688︰常值後置字元 'L' 無效; 找不到常值運算子或常值運算子範本 'operator ""L'
#define MACRO #define STRCAT(x, y) x\#\#y int main(){ auto *val1 = L"string"MACRO; auto *val2 = L"hello "L"world"; std::cout << STRCAT(L"hi ", L"there"); }若要修正錯誤,請將程式碼變更為新增一個空格:
#define MACRO // Remove ##. Strings are automatically // concatenated so they aren't needed #define STRCAT(x, y) x y int main(){ //Add space after closing quote auto *val1 = L"string" MACRO; auto *val2 = L"hello " L"world"; std::cout << STRCAT(L"hi ", L"there"); }在上述範例中,
MACRO不再被剖析為兩個符號(字串和巨集)。 現在會將它解析為單一符號 UDL。 這同樣適用於 L""L"",其先前會剖析為 L"" 和 L"",現在則剖析為 L""L 和 ""。字串串連規則也會符合標準,這表示 L"a" "b" 相當於 L"ab"。 舊版的 Visual Studio 不接受字元寬度不同之字串的串連。
已移除 C++11 空字符
現在,下列程式碼會產生錯誤 C2137:空字符常數
bool check(wchar_t c){ return c == L''; //implicit null character }若要修正錯誤,請變更程式碼以使 Null 更為明確:
bool check(wchar_t c){ return c == L'\0'; }無法依值攔截 MFC 例外狀況,因為其為不可複製
MFC 應用程式中的下列程式碼現在會造成錯誤 C2316︰'D': 無法捕捉,因為解構子和/或複製建構子不可存取或已被刪除
struct B { public: B(); private: B(const B &); }; struct D : public B { }; int main() { try { } catch (D) // C2316 { } }若要修正程式碼,您可以將 catch 區塊變更為
catch (const D &),但更好的解決方案通常是使用 MFC TRY/CATCH 巨集。alignof現在是關鍵字下列程式碼現在會產生錯誤 C2332:'class': 遺漏標記名稱。 若要修正程式碼,您必須將類別重新命名;或者,如果類別正在執行與
alignof相同的工作,則只需使用新的關鍵字來取代類別。class alignof{}constexpr現在是關鍵字下列程式碼現在會產生錯誤 C2059︰語法錯誤: ')'。 若要修正程式碼,您必須將任何名為
constexpr的函式或變數名稱重新命名。int constexpr() {return 1;}可移動的類型不能是 const
當函式傳回想要移動的類型時,其傳回類型不應是
const。刪除複製建構函式
下列程式碼現在會產生 C2280:'S::S(S &&)': 嘗試參考被刪除的函式:
struct S{ S(int, int); S(const S&) = delete; S(S&&) = delete; }; S s2 = S(2, 3); //C2280若要修正錯誤,請針對
S2使用直接初始化:struct S{ S(int, int); S(const S&) = delete; S(S&&) = delete; }; S s2 = {2,3}; //OK僅當沒有 lambda 擷取時才會產生函式指標的轉換
下列程式碼會在 Visual Studio 2015 中產生 C2664。
void func(int(*)(int)) {} int main() { func([=](int val) { return val; }); }若要修正錯誤,請從擷取清單中移除
=。涉及轉換運算子的不明確呼叫
下列程式碼現在會產生錯誤 C2440:「類型轉換」:無法從 'S2' 轉換成 'S1':
struct S1 { S1(int); }; struct S2 { operator S1(); operator int(); }; void f(S2 s2) { (S1)s2; }若要修正錯誤,請明確地呼叫轉換運算子:
void f(S2 s2) { //Explicitly call the conversion operator s2.operator S1(); // Or S1((int)s2); }下列程式碼現在會產生錯誤 C2593:「運算子 =」是模稜兩可的:
struct S1 {}; struct S2 { operator S1&(); operator S1() const; }; void f(S1 *p, S2 s) { *p = s; }若要修正錯誤,請明確地呼叫轉換運算子:
void f(S1 *p, S2 s) { *p = s.operator S1&(); }修正非靜態資料成員初始化 (NSDMI) 中無效的複製初始化
下列程式碼現在會產生 C2664 錯誤:'S1::S1(S1 &&)': 無法將引數 1 從 'bool' 轉換為 'const S1 &':
struct S1 { explicit S1(bool); }; struct S2 { S1 s2 = true; // error };若要修正錯誤,請使用直接初始化:
struct S2 { S1 s1{true}; // OK };存取 decltype 陳述式內的建構函式
下列程式現在會產生 C2248:'S::S': 無法存取類別 'S' 中的私有成員:
class S { S(); public: int i; }; class S2 { auto f() -> decltype(S().i); };若要修正錯誤,請在
S2中新增S的 friend 宣告:class S { S(); friend class S2; // Make S2 a friend public: int i; };lambda 預設的 ctor 會被隱含刪除
下列程式碼現在會產生錯誤 C3497︰您無法建構 Lambda 的執行個體:
void func(){ auto lambda = [](){}; decltype(lambda) other; }若要修正錯誤,無須呼叫預設建構函式。 如果 Lambda 不會擷取任何項目,則可將它轉換為函式指標。
具有已刪除指派運算子的 Lambda
下列程式碼現在會產生錯誤 C2280:
#include <memory> #include <type_traits> template <typename T, typename D> std::unique_ptr<T, typename std::remove_reference<D &&>::type> wrap_unique(T *p, D &&d); void f(int i) { auto encodedMsg = wrap_unique<unsigned char>(nullptr, [i](unsigned char *p) { }); encodedMsg = std::move(encodedMsg); }若要修正錯誤,請使用 functor 類別取代 lambda,或者無須使用指派運算子。
嘗試使用已刪除的複製建構函式來移動物件
下列程式碼現在會產生錯誤 C2280:'moveable::moveable(const moveable &)': 嘗試參考被刪除的函式
struct moveable { moveable() = default; moveable(moveable&&) = default; moveable(const moveable&) = delete; }; struct S { S(moveable && m) : m_m(m)//copy constructor deleted {} moveable m_m; };若要修正錯誤,請改為使用
std::move:S(moveable && m) : m_m(std::move(m))區域類別無法參考稍後在相同函式中定義的其他區域類別
下列程式碼現在會產生錯誤 C2079:'s' 使用未定義的結構體 'main::S2'
int main() { struct S2; struct S1 { void f() { S2 s; } }; struct S2 {}; }若要修正錯誤,請將
S2的定義往上移動:int main() { struct S2 { //moved up }; struct S1 { void f() { S2 s; } }; }無法在衍生的 ctor 主體中呼叫受保護的基底 ctor。
下列程式碼現在會產生錯誤 C2248:'S1::S1': 無法存取類別 'S1' 中所宣告之受保護的成員
struct S1 { protected: S1(); }; struct S2 : public S1 { S2() { S1(); } };若要修正錯誤,請在
S2中,從建構函式移除對S1()的呼叫,然後視需要將其放在另一個函式中。{} 防止轉換為指標
下列程式碼現在會產生 C2439:'S::p': 無法將成員初始化
struct S { S() : p({ 0 }) {} void *p; };若要修正錯誤,請移除
0周圍的括號,否則可改用nullptr,如下列範例所示:struct S { S() : p(nullptr) {} void *p; };含有括號的不正確巨集定義與使用方式
下列範例現在會產生錯誤 C2008:';': 未預期會出現在巨集定義中的字元
#define A; //cause of error struct S { A(); // error };若要修正問題,請將第一行變更為
#define A();下列程式碼會產生錯誤 C2059︰語法錯誤: ')'
//notice the space after 'A' #define A () ; struct S { A(); };若要修正程式碼,請移除 A 與 () 之間的空格。
下列程式碼會產生錯誤 C2091︰函式傳回函式:
#define DECLARE void f() struct S { DECLARE(); };若要修正錯誤,請在 S 中移除 DECLARE 之後的括號:
DECLARE;。下列程式碼會產生錯誤 C2062︰未預期的類型 'int'
#define A (int) struct S { A a; };若要修正問題,請定義
A,如下:#define A int宣告中額外的括弧
下列程式碼會產生錯誤 C2062︰類型 'int' 未預期的
struct S { int i; (int)j; };若要修正錯誤,請移除
j周圍的括弧。 如果為了清楚起見而需要括弧,則使用typedef。編譯器產生的建構函式和 __declspec(novtable)
在 Visual Studio 2015 中,當在具有虛擬基底類別的抽象類別中,由編譯器產生的內嵌建構函式與
__declspec(novtable)和__declspec(dllimport)結合使用時,可能較易暴露出不當使用的問題。auto 需要在直接列表初始化中使用單一表達式
下列程式碼現在會產生錯誤 C3518:'testPositions':在直接列表初始化的背景下,類型 'auto' 僅能從單一初始化表達式中推斷出來。
auto testPositions{ std::tuple<int, int>{13, 33}, std::tuple<int, int>{-23, -48}, std::tuple<int, int>{38, -12}, std::tuple<int, int>{-21, 17} };若要修正錯誤,一個可能方式是初始化
testPositions,如下:std::tuple<int, int> testPositions[]{ std::tuple<int, int>{13, 33}, std::tuple<int, int>{-23, -48}, std::tuple<int, int>{38, -12}, std::tuple<int, int>{-21, 17} };檢查 is_convertible 的類型與類型指標
下列程式碼現在會導致靜態判斷提示失敗。
struct B1 { private: B1(const B1 &); }; struct B2 : public B1 {}; struct D : public B2 {}; static_assert(std::is_convertible<D, B2>::value, "fail");若要修正錯誤,請變更
static_assert,使它能夠比較D和B2的指標:static_assert(std::is_convertible<D*, B2*>::value, "fail");__declspec(novtable) 宣告必須一致
__declspec宣告在所有程式庫之間必須一致。 下列程式碼現在會產生單一定義規則 (ODR) 違規://a.cpp class __declspec(dllexport) A { public: A(); A(const A&); virtual ~A(); private: int i; }; A::A() {} A::~A() {} A::A(const A&) {} //b.cpp // compile with cl.exe /nologo /LD /EHsc /Osx b.cpp #pragma comment(lib, "A") class __declspec(dllimport) A { public: A(); A(const A&); virtual ~A(); private: int i; }; struct __declspec(novtable) __declspec(dllexport) B : virtual public A { virtual void f() = 0; }; //c.cpp #pragma comment(lib, "A") #pragma comment(lib, "B") class __declspec(dllimport) A { public: A(); A(const A&); virtual ~A(); private: int i; }; struct /* __declspec(novtable) */ __declspec(dllimport) B // Error. B needs to be novtable here also. : virtual public A { virtual void f() = 0; }; struct C : virtual B { virtual void f(); }; void C::f() {} C c;
Update 1 的一致性改善
私用的虛擬基底類別與間接繼承
舊版編譯器允許衍生類別呼叫其間接衍生之
private virtual基底類別的成員函式。 這個舊的行為不正確且不符合 C++ 標準。 編譯器不再接受以這種方式撰寫的程式碼,且會發出編譯器錯誤 C2280。error C2280: 'void *S3::__delDtor(unsigned int)': attempting to reference a deleted function範例 (之前)
class base { protected: base(); ~base(); }; class middle : private virtual base {}; class top : public virtual middle {}; void destroy(top *p) { delete p; // }範例 (之後)
class base; // as above class middle : protected virtual base {}; class top : public virtual middle {}; void destroy(top *p) { delete p; }- 或 -
class base; // as above class middle : private virtual base {}; class top : public virtual middle, private virtual bottom {}; void destroy(top *p) { delete p; }多載的運算子 new 和運算子 delete
編譯器先前版本允許非成員 operator new 和非成員 operator delete 宣告為 static,以及在全域命名空間以外的命名空間中宣告。 這種舊行為造成的風險是,程式不會呼叫程式設計人員所預期的
new或delete運算子實作,導致無訊息的錯誤執行階段行為。 編譯器不再接受以這種方式撰寫的程式碼,並會發出編譯器錯誤 C2323。error C2323: 'operator new': non-member operator new or delete functions may not be declared static or in a namespace other than the global namespace.範例 (之前)
static inline void * __cdecl operator new(size_t cb, const std::nothrow_t&) // error C2323範例 (之後)
void * __cdecl operator new(size_t cb, const std::nothrow_t&) // removed 'static inline'此外,雖然編譯器不提供特定的診斷,但內嵌運算子
new會被視為語式錯誤。對非類別類型呼叫 'operator type()' (使用者定義的轉換)
舊版編譯器允許在非類別類型上呼叫 'operator type()',但以無訊息方式略過。 這種舊行為造成的風險是,會產生無訊息的錯誤程式碼,導致無法預期的執行階段行為。 編譯器不再接受以這種方式撰寫的程式碼,並會發出編譯器錯誤 C2228。
error C2228: left of '.operator type' must have class/struct/union範例 (之前)
typedef int index_t; void bounds_check(index_t index); void login(int column) { bounds_check(column.operator index_t()); // error C2228 }範例 (之後)
typedef int index_t; void bounds_check(index_t index); void login(int column) { bounds_check(column); // removed cast to 'index_t', 'index_t' is an alias of 'int' }冗餘的類型名稱在詳細類型說明符中
舊版編譯器允許複雜類型指定名稱中有
typename,但以此方式撰寫的程式碼語意不正確。 編譯器不再接受以這種方式撰寫的程式碼,並會發出編譯器錯誤 C3406。error C3406: 'typename' cannot be used in an elaborated type specifier範例 (之前)
template <typename class T> class container;範例 (之後)
template <class T> // alternatively, could be 'template <typename T>'; 'typename' is not elaborating a type specifier in this case class container;從初始設定式清單推斷陣列類型
舊版編譯器不支援初始設定式清單的陣列類型推斷。 編譯器現在支援這種形式的類型推斷,結果是使用初始化列表呼叫函式範本時,可能會變得模稜兩可,或者選擇的多載會與舊版編譯器不同。 若要解決這些問題,程式現在必須明確指定程式設計人員所要的多載。
當這種新行為使重載解析考慮到與歷史候選項目同樣出色的其他候選項目時,呼叫就會變得模稜兩可,並因此產生編譯錯誤 C2668。
error C2668: 'function' : ambiguous call to overloaded function.範例 1:模稜兩可地呼叫多載函式 (之前)
// In previous versions of the compiler, code written in this way would unambiguously call f(int, Args...) template < typename... Args> void f(int, Args...); // template < int N, typename... Args> void f(const int(&)[N], Args...); int main() { // The compiler now considers this call ambiguous, and issues a compiler error f({ 3 }); error C2668 : 'f' ambiguous call to overloaded function }範例 1:模稜兩可地呼叫多載函式 (之後)
template < typename... Args> void f(int, Args...); // template < int N, typename... Args> void f(const int(&)[N], Args...); int main() { // To call f(int, Args...) when there is just one expression in the initializer list, remove the braces from it. f(3); }當這種新行為讓過載解析考慮額外的候選項目,而這些項目比歷史候選項目更適合時,呼叫會明確無誤地解析為新的候選項目,導致程序行為發生變化,這可能與程式設計人員的原意不同。
範例 2:重載解析的變更 (之前)
// In previous versions of the compiler, code written in this way would unambiguously call f(S, Args...) struct S { int i; int j; }; template < typename... Args> void f(S, Args...); template < int N, typename... Args> void f(const int *&)[N], Args...); int main() { // The compiler now resolves this call to f(const int (&)[N], Args...) instead f({ 1, 2 }); }範例 2:重載解析的變更 (之後)
struct S; // as before template < typename... Args> void f(S, Args...); template < int N, typename... Args> void f(const int *&)[N], Args...); int main() { // To call f(S, Args...), perform an explicit cast to S on the initializer list. f(S{ 1, 2 }); }恢復 switch 陳述式警告
舊版編譯器中移除了某些與
switch陳述式相關的警告;現在已還原這些警告。 編譯器現在會發出還原的警告,而與特定情況相關的警告 (包括預設的情況) 都會在包含違規情況的程式行發出,而不是在 switch 陳述式的最後一行發出。 由於現在在不同程式行中發出警告,以前使用#pragma warning(disable:####)隱藏的警告可能無法如預期一樣被隱藏起來。 若想要如預期隱藏這些警告,就必須將#pragma warning(disable:####)指示詞移至第一個違規情況的上一行。 以下是恢復的警告:warning C4060: switch statement contains no 'case' or 'default' labelswarning C4061: enumerator 'bit1' in switch of enum 'flags' is not explicitly handled by a case labelwarning C4062: enumerator 'bit1' in switch of enum 'flags' is not handledwarning C4063: case 'bit32' is not a valid value for switch of enum 'flags'warning C4064: switch of incomplete enum 'flags'warning C4065: switch statement contains 'default' but no 'case' labelswarning C4808: case 'value' is not a valid value for switch condition of type 'bool'Warning C4809: switch statement has redundant 'default' label; all possible 'case' labels are givenC4063 範例 (之前)
class settings { public: enum flags { bit0 = 0x1, bit1 = 0x2, ... }; ... }; int main() { auto val = settings::bit1; switch (val) { case settings::bit0: break; case settings::bit1: break; case settings::bit0 | settings::bit1: // warning C4063 break; } }C4063 範例 (之後)
class settings { ... }; // as above int main() { // since C++11, use std::underlying_type to determine the underlying type of an enum typedef std::underlying_type< settings::flags> ::type flags_t; auto val = settings::bit1; switch (static_cast< flags_t> (val)) { case settings::bit0: break; case settings::bit1: break; case settings::bit0 | settings::bit1: // ok break; } };其他還原警告的範例將在它們的文件中提供。
#include:使用父目錄指示符「..」 在路徑名稱中 (只會影響
/Wall/WX)舊版編譯器未偵測到使用父目錄指示符'..' 在
#include指示詞的路徑名稱中。 以這種方式撰寫的程式碼通常會包含因為錯誤使用專案相對路徑而存在於專案以外的標頭。 這種舊行為造成的風險是,編譯程式時所包含的原始程式檔,可能不是程式設計人員想要的檔案,或是這些相對路徑無法移植到其他建置環境。 編譯器現在會偵測以這種方式撰寫的程式碼,並通知程式設計人員,如已啟用,還會發出選擇性的編譯器警告 C4464。warning C4464: relative include path contains '..'範例 (之前)
#include "..\headers\C4426.h" // emits warning C4464範例 (之後)
#include "C4426.h" // add absolute path to 'headers\' to your project's include directories此外,雖然編譯器不會提供特定的診斷,但仍建議您不要使用上層目錄指定名稱 ".." 來指定專案的 include 目錄。
#pragma optimize() 超過了標頭文件的結尾範圍 (只會影響
/Wall/WX)舊版編譯器未偵測到最佳化旗標設定的變更,這會逸出包含在轉譯單位內的標頭檔。 編譯器現在會偵測以這種方式撰寫的程式碼,並通知程式設計人員,如已啟用,還會在違反
#include的位置發出選擇性的編譯器警告 C4426。 只有當變更與編譯器命令列引數設定的最佳化旗標發生衝突時,才會發出這個警告。warning C4426: optimization flags changed after including header, may be due to #pragma optimize()範例 (之前)
// C4426.h #pragma optimize("g", off) ... // C4426.h ends // C4426.cpp #include "C4426.h" // warning C4426範例 (之後)
// C4426.h #pragma optimize("g", off) ... #pragma optimize("", on) // restores optimization flags set via command-line arguments // C4426.h ends // C4426.cpp #include "C4426.h"不相符的 #pragma warning(push) 和 #pragma warning(pop) (只會影響
/Wall/WX)舊版編譯器無法偵測到在不同原始檔案中,
#pragma warning(push)狀態變更是否與#pragma warning(pop)狀態變更相配對,這種情況極少是刻意為之。 這種舊行為造成的風險是,編譯程式所啟用的警告集合不是程式設計人員想要的集合,可能導致無訊息的錯誤執行階段行為。 現在編譯器會偵測以此方式撰寫的程式碼,通知其程式設計人員,同時在符合#pragma warning(pop)的位置發出選擇性的編譯器警告 C5031 (如有啟用)。 此警告包含一個附註,註明對應的 #pragma warning(push) 的位置。warning C5031: #pragma warning(pop): likely mismatch, popping warning state pushed in different file範例 (之前)
// C5031_part1.h #pragma warning(push) #pragma warning(disable:####) ... // C5031_part1.h ends without #pragma warning(pop) // C5031_part2.h ... #pragma warning(pop) // pops a warning state not pushed in this source file ... // C5031_part1.h ends // C5031.cpp #include "C5031_part1.h" // leaves #pragma warning(push) 'dangling' ... #include "C5031_part2.h" // matches 'dangling' #pragma warning(push), resulting in warning C5031 ...範例 (之後)
// C5031_part1.h #pragma warning(push) #pragma warning(disable:####) ... #pragma warning(pop) // pops the warning state pushed in this source file // C5031_part1.h ends without #pragma warning(pop) // C5031_part2.h #pragma warning(push) // pushes the warning state pushed in this source file #pragma warning(disable:####) ... #pragma warning(pop) // C5031_part1.h ends // C5031.cpp #include "C5031_part1.h" // #pragma warning state changes are self-contained and independent of other source files or their #include order. ... #include "C5031_part2.h" ...以此方式撰寫的程式碼雖然不常見,但有時是刻意為之。 以此方式撰寫的程式碼對於
#include順序的變更十分敏感;如有可能,建議原始程式碼檔案獨立管理警告狀態。未匹配的 #pragma warning(push) (只會影響
/Wall/WX)舊版編譯器在翻譯單元結尾未能偵測到不相符的
#pragma warning(push)狀態變更。 現在編譯器會偵測以此方式撰寫的程式碼並通知其程式設計人員,同時在不相符的#pragma warning(push)位置發出選擇性的編譯器警告 C5032 (如有啟用)。 只有轉譯單位沒有任何編譯錯誤時,才會發出這個警告。warning C5032: detected #pragma warning(push) with no corresponding #pragma warning(pop)範例 (之前)
// C5032.h #pragma warning(push) #pragma warning(disable:####) ... // C5032.h ends without #pragma warning(pop) // C5032.cpp #include "C5032.h" ... // C5032.cpp ends -- the translation unit is completed without #pragma warning(pop), resulting in warning C5032 on line 1 of C5032.h範例 (之後)
// C5032.h #pragma warning(push) #pragma warning(disable:####) ... #pragma warning(pop) // matches #pragma warning (push) on line 1 // C5032.h ends // C5032.cpp #include "C5032.h" ... // C5032.cpp ends -- the translation unit is completed without unmatched #pragma warning(push)由於 #pragma 警告狀態追蹤能力的改善,可能會發出其他警告
舊版編譯器過去追蹤 #pragma 警告狀態變更的能力不佳,無法發出所有預期出現的警告。 這種行為帶來的風險在於,某些警告在非程式員預期的情況下可能會被有效地抑制。 編譯器現在追蹤
#pragma warning狀態的能力更強大,特別是關於範本內部的#pragma warning狀態變更,可以選擇性發出新的警告 C5031 和 C5032,其目的是協助程式設計人員找出非預期使用的#pragma warning(push)和#pragma warning(pop)。在
#pragma warning狀態變更追蹤經過改良之後,以往不當隱藏的警告或先前與誤判問題有關的警告,現在都能夠發出。不可能執行到之程式碼的識別改善
C++ 標準程式庫的變更,以及比舊版編譯器強大的內嵌函式呼叫能力,可能會讓編譯器證明不可能執行到某些程式碼。 這種新行為可能會導致出現新的警告 C4720 例,而且此警告將被更頻繁地發出。
warning C4720: unreachable code在許多情況下,只有在啟用最佳化編譯時才可能發出這個警告,因為最佳化可能內嵌更多的函式呼叫、消除多餘的程式碼,或者使得某些程式碼被判斷為無法到達。 我們觀察到,新的警告 C4720 實例經常出現於 try/catch 區塊,尤其是在使用 std::find 時。
範例 (之前)
try { auto iter = std::find(v.begin(), v.end(), 5); } catch (...) { do_something(); // ok }範例 (之後)
try { auto iter = std::find(v.begin(), v.end(), 5); } catch (...) { do_something(); // warning C4702: unreachable code }
Update 2 的一致性改善
由於僅部分支援運算式 SFINAE,因此可能會發出額外的警告與錯誤
舊版編譯器因為缺少對運算式 SFINAE 的支援,所以無法剖析
decltype指定名稱內某些種類的運算式。 這個舊的行為不正確且不符合 C++ 標準。 編譯器現在會剖析這些運算式,並因持續的相容性改進而能部分支援運算式 SFINAE。 如此一來,編譯器現在就會發出在舊版編譯器未剖析的運算式中找到的警告與錯誤。當這個新行為剖析包含尚未宣告類型的
decltype運算式時,編譯器就會發出編譯器錯誤 C2039。error C2039: 'type': is not a member of 'global namespace'範例 1:使用未宣告的型別 (之前)
struct s1 { template < typename T> auto f() - > decltype(s2< T> ::type::f()); // error C2039 template< typename> struct s2 {}; }範例 1 (之後)
struct s1 { template < typename> // forward declare s2struct s2; template < typename T> auto f() - > decltype(s2< T> ::type::f()); template< typename> struct s2 {}; }當這個新行為剖析一個
typename運算式,而該運算式缺少必須使用的decltype關鍵字來指定相依名稱為類型時,編譯器就會發出編譯器警告 C4346 以及編譯器錯誤 C2923。warning C4346: 'S2<T>::Type': dependent name is not a typeerror C2923: 's1': 'S2<T>::Type' is not a valid template type argument for parameter 'T'範例 2:相依名稱不是類型 (之前)
template < typename T> struct s1 { typedef T type; }; template < typename T> struct s2 { typedef T type; }; template < typename T> T declval(); struct s { template < typename T> auto f(T t) - > decltype(t(declval< S1< S2< T> ::type> ::type> ())); // warning C4346, error C2923 };範例 2 (之後)
template < typename T> struct s1 { ... }; // as above template < typename T> struct s2 { ... }; // as above template < typename T> T declval(); struct s { template < typename T> auto f(T t) - > decltype(t(declval< S1< typename S2< T> ::type> ::type> ())); };volatile成員變數會禁止隱含定義的建構函式與指派運算子舊版編譯器允許具有
volatile成員變數的類別自動產生預設的複製/移動建構函式及預設的複製/移動指派運算子。 這個舊的行為不正確且不符合 C++ 標準。 編譯器現在會考慮讓具有volatile成員變數的類別具有非一般建構和指派運算子,其可防止自動產生這些運算子的預設實作。 當此類別是等位 (或類別內的匿名等位) 的成員時,就會將等位 (或包含匿名等位的類別) 的複製/移動建構函式和複製/移動指派運算子隱含定義為已刪除。 在未明確定義的情況下,嘗試建構或複製等位 (或包含匿名等位的類別) 將會發生錯誤,導致編譯器發出編譯器錯誤 C2280。error C2280: 'B::B(const B &)': attempting to reference a deleted function範例 (之前)
struct A { volatile int i; volatile int j; }; extern A* pa; struct B { union { A a; int i; }; }; B b1{ *pa }; B b2(b1); // error C2280範例 (之後)
struct A { int i; int j; }; extern volatile A* pa; A getA() // returns an A instance copied from contents of pa { A a; a.i = pa - > i; a.j = pa - > j; return a; } struct B; // as above B b1{ GetA() }; B b2(b1); // error C2280靜態成員函式不支援 cv 限定詞
舊版 Visual Studio 2015 允許靜態成員函式擁有 cv 限定詞。 此行為起因於 Visual Studio 2015 與 Visual Studio 2015 Update 1 的迴歸;Visual Studio 2013 與舊版編譯器拒絕以此方式撰寫的程式碼。 Visual Studio 2015 與 Visual Studio 2015 Update 1 的行為不正確且不符合 C++ 標準。 Visual Studio 2015 Update 2 拒絕以此方式撰寫的程式碼,並會發出編譯器錯誤 C2511。
error C2511: 'void A::func(void) const': overloaded member function not found in 'A'範例 (之前)
struct A { static void func(); }; void A::func() const {} // C2511範例 (之後)
struct A { static void func(); }; void A::func() {} // removed constWinRT 程式碼不允許列舉的前置宣告 (只會影響
/ZW)針對 Windows 執行階段 (WinRT) 編譯的程式碼不允許預先宣告
enum類型,類似於當受控 C++ 程式碼使用/clr編譯器開關編譯至 .NET Framework 的情形。 此行為確保始終知道列舉的大小,並能正確映射到 WinRT 類型系統。 編譯器拒絕以此方式撰寫的程式碼,且會發出編譯器錯誤 C2599 以及編譯器錯誤 C3197。error C2599: 'CustomEnum': the forward declaration of a WinRT enum is not allowederror C3197: 'public': can only be used in definitions範例 (之前)
namespace A { public enum class CustomEnum : int32; // forward declaration; error C2599, error C3197 } namespace A { public enum class CustomEnum : int32 { Value1 }; } public ref class Component sealed { public: CustomEnum f() { return CustomEnum::Value1; } };範例 (之後)
// forward declaration of CustomEnum removed namespace A { public enum class CustomEnum : int32 { Value1 }; } public ref class Component sealed { public: CustomEnum f() { return CustomEnum::Value1; } };不得以內嵌方式宣告多載的非成員運算子 new 與運算子 delete (層級 1 (
/W1) 預設為開啟)舊版編譯器不會在以內嵌方式宣告非成員 operator new 與 operator delete 函式時發出警告。 以此方式撰寫的程式碼格式不正確(不需要診斷),可能會因為 new 和 delete 運算子不匹配(特別是與指定大小的解除配置一起使用時)而導致記憶體問題,這些問題難以診斷。 編譯器現在會發出編譯器警告 C4595,協助識別以此方式撰寫的程式碼。
warning C4595: 'operator new': non-member operator new or delete functions may not be declared inline範例 (之前)
inline void* operator new(size_t sz) // warning C4595 { ... }範例 (之後)
void* operator new(size_t sz) // removed inline { ... }可能需要先將運算子定義從標頭檔移出,再將其移入對應的原始程式檔,才能修正以此方式撰寫的程式碼。
第三次更新中的一致性改善
std::is_convertable 現在已可偵測自我指派 (標準程式庫)
當舊版
std::is_convertable類型特性的複製建構函式被刪除或為私用時,其無法正確地偵測類別類型的自我指派。 現在在套用到複製建構函式被刪除或為私用的類別類型時,std::is_convertable<>::value會正確設定為false。此變更沒有相關聯的編譯器診斷。
範例
#include <type_traits> class X1 { public: X1(const X1&) = delete; }; class X2 { private: X2(const X2&); }; static_assert(std::is_convertible<X1&, X1>::value, "BOOM");static_assert(std::is_convertible<X2&, X2>::value, "BOOM");在舊版編譯器中,因為
std::is_convertable<>::value不正確地設定為true,因此此範例底部的靜態斷言通過。std::is_convertable<>::value現在會正確地設定為false,導致靜態斷言失敗。預設或已刪除的無特定邏輯的複製及移動建構函式會遵守存取修飾符
舊版編譯器在允許呼叫時不會先檢查預設或已刪除之簡單複製及移動建構子的存取控制子。 這個舊的行為不正確且不符合 C++ 標準。 在某些情況下,這項舊行為可能導致無提示的錯誤程式碼生成,從而造成執行階段的不可預測行為。 編譯器現在會對預設或刪除的 trivial 複製及移動建構函式進行存取指定名稱檢查,據此決定其是否可以接受呼叫;若無法呼叫,即發出編譯器警告 C2248。
error C2248: 'S::S' cannot access private member declared in class 'S'範例 (之前)
class S { public: S() = default; private: S(const S&) = default; }; void f(S); // pass S by value int main() { S s; f(s); // error C2248, can't invoke private copy constructor }範例 (之後)
class S { public: S() = default; private: S(const S&) = default; }; void f(const S&); // pass S by reference int main() { S s; f(s); }有屬性 ATL 程式碼支援已被棄用 (層級 1 (
/W1) 預設為開啟)舊版編譯器支援屬性化 ATL 程式碼。 由於移除屬性化 ATL 程式碼的支援已進入下一階段(從 Visual Studio 2008 開始),因此屬性化 ATL 程式碼已被棄用。 編譯器現在會發出編譯器警告 C4467,協助識別這類已標示為即將淘汰的程式碼。
warning C4467: Usage of ATL attributes is deprecated若要繼續使用屬性化 ATL 程式碼直到從編譯器移除支援為止,可將
/Wv:18或/wd:4467命令列引數傳遞給編譯器,或在原始程式碼中新增#pragma warning(disable:4467)來停用此警告。範例 1 (之前)
[uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")] class A {};範例 1 (之後)
__declspec(uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")) A {};您偶爾可能會需要或想要建立 IDL 檔案,以避免使用已標示為即將淘汰的 ATL 屬性,如下列範例程式碼所示
範例 2 (之前)
[emitidl]; [module(name = "Foo")]; [object, local, uuid("9e66a290-4365-11d2-a997-00c04fa37ddb")] __interface ICustom { HRESULT Custom([in] long l, [out, retval] long *pLong); [local] HRESULT CustomLocal([in] long l, [out, retval] long *pLong); }; [coclass, appobject, uuid("9e66a294-4365-11d2-a997-00c04fa37ddb")] class CFoo : public ICustom { // ... };首先,建立 *.idl 檔案;產生的 vc140.idl 檔案可用於取得包含介面及註釋的 *.idl 檔案。
接著請在您的組建中新增 MIDL 步驟,確保 C++ 介面定義能夠順利產生。
範例 2 IDL (之後)
import "docobj.idl"; [ object, local, uuid(9e66a290 - 4365 - 11d2 - a997 - 00c04fa37ddb) ] interface ICustom : IUnknown { HRESULT Custom([in] long l, [out, retval] long *pLong); [local] HRESULT CustomLocal([in] long l, [out, retval] long *pLong); }; [version(1.0), uuid(29079a2c - 5f3f - 3325 - 99a1 - 3ec9c40988bb)] library Foo { importlib("stdole2.tlb"); importlib("olepro32.dll"); [ version(1.0), appobject,uuid(9e66a294 - 4365 - 11d2 - a997 - 00c04fa37ddb) ] coclass CFoo { interface ICustom; }; }然後請直接在實作檔案中使用 ATL,如下列範例程式碼所示。
範例 2 實作 (之後)
#include <idl.header.h> #include <atlbase.h> class ATL_NO_VTABLE CFooImpl : public ICustom, public ATL::CComObjectRootEx< CComMultiThreadModel> { public: BEGIN_COM_MAP(CFooImpl) COM_INTERFACE_ENTRY(ICustom) END_COM_MAP() };先行編譯標頭 (PCH) 檔與不相符的 #include 指示詞 (只會影響
/Wall/WX)舊版編譯器在使用先行編譯的標頭檔 (PCH) 時,會接受原始程式碼中在
-Yc和-Yu兩次編譯過程之間不一致的#include指示詞。 編譯器現在已不再接受以此方式撰寫的程式碼。 現在在使用 PCH 檔案時,編譯器會發出編譯器警告 CC4598 協助識別不相符的#include指示詞。warning C4598: 'b.h': included header file specified for Ycc.h at position 2 does not match Yuc.h at that position範例 (之前):
X.cpp (-Ycc.h)
#include "a.h" #include "b.h" #include "c.h"Z.cpp (-Yuc.h)
#include "b.h" #include "a.h" // mismatched order relative to X.cpp #include "c.h"範例 (之後)
X.cpp (-Ycc.h)
#include "a.h" #include "b.h" #include "c.h"Z.cpp (-Yuc.h)
#include "a.h" #include "b.h" // matched order relative to X.cpp #include "c.h"先行編譯標頭檔 (PCH) 與不相符的 #include 目錄 (只會影響
/Wall/WX)在使用預編譯標頭檔 (PCH) 時,舊版編譯器會在
-Yc和-Yu編譯之間接受不匹配的 include 目錄 (-I) 命令列引數。 編譯器現在已不再接受以此方式撰寫的程式碼。 現在在使用 PCH 檔案時,編譯器會發出編譯器警告 CC4599 協助識別不相符的 include 目錄 (-I) 命令列引數。warning C4599: '-I..' : specified for Ycc.h at position 1 does not match Yuc.h at that position範例 (之前)
cl /c /Wall /Ycc.h -I.. X.cpp cl /c /Wall /Yuc.h Z.cpp範例 (之後)
cl /c /Wall /Ycc.h -I.. X.cpp cl /c /Wall /Yuc.h -I.. Z.cpp
Visual Studio 2013 一致性變更
編譯器
final 關鍵字現在會在先前編譯過的位置產生無法解析的符號錯誤:
struct S1 { virtual void f() = 0; }; struct S2 final : public S1 { virtual void f(); }; int main(S2 *p) { p->f(); }在舊版中,由於呼叫為
virtual呼叫,因此不會發出錯誤;儘管如此,程式還是會在執行階段損毀。 現在,由於類別已知為 final,因此會發生連結器錯誤。 在這個範例中,為了修正錯誤,您需要鏈接包含S2::f定義的目標文件。當您在命名空間中使用 friend 函式時,必須先重新宣告 friend 函式才能加以參考,否則將會收到錯誤;這是因為編譯器現在遵循 ISO C++ 標準。 例如,此範例不再編譯:
namespace NS { class C { void func(int); friend void func(C* const) {} }; void C::func(int) { NS::func(this); // error } }若要修正此程式碼,請宣告
friend函式:namespace NS { class C { void func(int); friend void func(C* const) {} }; void func(C* const); // conforming fix void C::func(int) { NS::func(this); }此 C++ 標準不允許在類別中明確特製化。 雖然 Microsoft C++ 編譯器在某些情況下允許這種做法,但是在像下列範例這樣的情況下,現在就會產生錯誤,因為編譯器不會將第二個函式視為第一個函式的特製化。
template < int N> class S { public: template void f(T& val); template < > void f(char val); }; template class S< 1>;若要更正這個程式碼,請修改第二個函式:
template <> void f(char& val);編譯器不會再嘗試清楚區別下列範例中的兩個函式,且現在還會發出錯誤:
template< typename T> void Func(T* t = nullptr); template< typename T> void Func(...); int main() { Func< int>(); // error }若要更正這個程式碼,請釐清呼叫:
template< typename T> void Func(T* t = nullptr); template< typename T> void Func(...); int main() { Func< int>(nullptr); // ok }在編譯器符合 ISO C++11 規範之前,下列程式碼會進行編譯並導致
x解析為int類型:auto x = {0}; int y = x;此程式碼現在會將
x解析為std::initializer_list<int>類型,因而導致在下一行嘗試將x指派給int類型時發生錯誤。 (預設不會進行轉換)。若要更正此程式碼,使用int來取代auto:int x = {0}; int y = x;當右側值類型不符合左側要初始化的值類型時,將無法再彙總初始化且會發出錯誤;因為根據 ISO C++11 標準的要求,初始化必須在不縮小轉換的情況下統一執行。 以前,如果可以使用縮小轉換,則會發出編譯器警告 (層級 4) C4242,而不是錯誤。
int i = 0; char c = {i}; // error若要更正這個程式碼,請加入明確的縮小轉換:
int i = 0; char c = {static_cast<char>(i)};下列初始化已無法再執行:
void *p = {{0}};若要更正這個程式碼,請使用下列任一形式:
void *p = 0; // or void *p = {0};名稱查閱已變更。 Visual Studio 2012 與 Visual Studio 2013 中的 C++ 編譯器 會以不同方式解析下列程式碼︰
enum class E1 { a }; enum class E2 { b }; int main() { typedef E2 E1; E1::b; }在 Visual Studio 2012 中,運算式
E1中的E1::b會在全域範圍中解析為::E1。 Visual Studio 2013 中,E1在運算式E1::b中解析為typedef E2中的main()定義,且具有::E2類型。物件配置已變更。 在 x64 上,類別的物件配置可能會和先前的版本不同。 如果其具有
virtual函式,但不具含有virtual函式的基底類別,編譯器的物件模型就會在資料成員配置之後,插入指向virtual函式資料表的指標。 這表示該配置可能無法在所有情況下都是最佳。 在舊版中,一項針對 x64 的最佳化項目會嘗試為您改善配置,但因為該項目在複雜的程式碼中無法正確運作,所以在 Visual Studio 2013 中已將其移除。 例如,請參考這個程式碼:__declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };在 Visual Studio 2013 中,x64 上
sizeof(S2)的結果為 48,但在舊版中會評估為 32。 若要在 x64 版的 Visual Studio 2013 C++ 編譯器中將此評估為 32,請新增含有virtual函式的虛擬基底類別:__declspec(align(16)) struct S1 { }; struct dummy { virtual ~dummy() {} }; struct S2 : public dummy { virtual ~S2(); void *p; S1 s; };若要在程式碼中尋找舊版本已嘗試進行最佳化的地方,搭配
/W3編譯器選項一起使用該版本的編譯器,並開啟警告 C4370。 例如:#pragma warning(default:4370) __declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };在 Visual Studio 2013 前,此程式碼會輸出此訊息:「警告 C4370: 'S2': 因為可提供較佳的封裝,類別配置可能已在舊版本的編譯器中變更」。
在所有版本的編譯器中,x86 編譯器都具有相同的布局次佳問題。 例如,如果這個程式碼是為 x86 而編譯:
struct S { virtual ~S(); int i; double d; };sizeof(S)的結果會是 24。 不過,如果您使用提到的 x64 因應措施,則可以減少為 16:struct dummy { virtual ~dummy() {} }; struct S : public dummy { virtual ~S(); int i; double d; };
標準程式庫
Visual Studio 2013 中的 C++ 編譯器可偵測 _ITERATOR_DEBUG_LEVEL 中不符的情況 (實作於 Visual Studio 2010 中) 以及 RuntimeLibrary 不符的情況。 當編譯器選項 /MT (靜態發行)、/MTd (靜態偵錯)、/MD (動態發行) 和 /MDd (動態偵錯) 混和時,就會發生這些不相符的情況。
若您的程式碼認可舊版的模擬別名範本,必須加以變更。 例如,現在您必須改成
allocator_traits<A>::rebind_alloc<U>::other,而不是allocator_traits<A>::rebind_alloc<U>。 雖然已不再需要ratio_add<R1, R2>::type,而且現在建議您使用ratio_add<R1, R2>,但前者還是會進行編譯,因為ratio<N, D>必須有縮減一定比例的「類型」typedef (如果已經縮減,則會是相同類型)。在呼叫
#include <algorithm>或std::min()時,您必須使用std::max()。若您的現有程式碼使用前一版本的模擬作用域列舉型態——傳統不限範圍的列舉包裝在命名空間中——就必須進行變更。 例如,如果您原本參考的是
std::future_status::future_status類型,那麼現在必須改為std::future_status。 不過,大部分的程式碼不會受影響,例如std::future_status::ready仍會編譯。explicit operator bool()會比運算子 unspecified-bool-type() 更為嚴格。explicit operator bool()允許進行明確的 bool 轉換(例如,假設有一個shared_ptr<X> sp,則static_cast<bool>(sp)和bool b(sp)都是有效的),以及布林值可測試的「情境轉換」到 bool(例如if (sp)、!sp、sp &&等)。 不過,explicit operator bool()會禁止隱含轉換成 bool,因此您不能使用bool b = sp;,且假設傳回類型為 bool,則您不能使用return sp。由於現在已經實作真正的 variadic 範本,所以 _VARIADIC_MAX 與相關的巨集不會有任何作用。 如果您仍然定義 _VARIADIC_MAX,則將會被忽略。 如果您承認我們的巨集系統旨在以任何其他方式支援模擬的「variadic templates」,那麼您必須修改您的程式碼。
除了普通關鍵字之外,C++ 標準程式庫標頭現在禁止對上下文敏感的關鍵字 override 和 final 進行巨集替換。
reference_wrapper、ref()及cref()現在禁止繫結至暫存物件。<random>現在嚴格執行其在編譯時期的前置條件。各種不同的 C++ 標準程式庫類型特性都有「T 應為完整的類型」這項前置條件。 雖然編譯器現在會更嚴格實施這項先決條件,但並非在所有情況中都能實施。 (因為 C++ 標準程式庫前置條件違規會觸發未經定義的行為,所以這項標準無法保證一定能夠實施)。
C++ 標準程式庫不支援
/clr:oldSyntax。common_type<>的 C++11 規格會產生未預期和不想要的結果,尤其是使common_type<int, int>::type傳回int&&。 因此,編譯器會實作程式庫工作群組問題 2141 的建議解決方式,也就是使common_type<int, int="">::type傳回int。此變更的副作用是身分識別案例不再適用 (
common_type<T>不一定會產生T類型)。 此行為符合建議的解決方式,不過,其會中斷依賴之前行為的任何程式碼。如果您需要識別類型特性,請不要使用
std::identity中定義的非標準<type_traits>,因為它不適用<void>。 請依照您的需求,實作一個符合您要求的身份類型特性。 以下是範例:template < typename T> struct Identity { typedef T type; };
MFC 和 ATL
僅限 Visual Studio 2013:因為 Unicode 現已相當普遍且 MBCS 的使用率大幅降低,所以 Visual Studio 不會隨附 MFC MBCS 程式庫。 這項變更也讓 MFC 與 Windows SDK 本身更為相符,因為許多新的控制項和訊息都限用 Unicode。 不過,如果您必須繼續使用 MBCS MFC 程式庫,您可以從 Microsoft 下載中心下載:適用於 Visual Studio 2013 的多位元組 MFC 程式庫。 Visual C++ 可轉散發套件仍然包含這個程式庫。 (注意:在 Visual Studio 2015 和較新版中,MBCS DLL 會隨附於 C++ 安裝程式元件)。
MFC 功能區的可及性已被更改。 一改過去的單層架構,現在改為階層式架構。 您仍然可以藉由呼叫
CRibbonBar::EnableSingleLevelAccessibilityMode()使用舊有行為。CDatabase::GetConnect方法已移除。 為使安全性變得更好,連接字串現在會加密儲存且只在必要時解密;其無法再以純文字格式傳回。 字串可透過使用CDatabase::Dump方法來取得。CWnd::OnPowerBroadcast的簽章已變更。 這個訊息處理常式的簽名已變更為接受 LPARAM 作為第二個參數。簽章已變更為可容納訊息處理常式。 下列函式的參數清單已變更為使用新加入的 ON_WM_* 訊息處理常式:
CWnd::OnDisplayChange已變更為 (UINT, int, int),而不再是 (WPARAM, LPARAM),所以可在訊息映射表中使用新的 ON_WM_DISPLAYCHANGE 巨集。CFrameWnd::OnDDEInitiate已變更為 (CWnd*, UINT, UINT),而不再是 (WPARAM, LPARAM),因此可在訊息映射中使用新的 ON_WM_DDE_INITIATE 巨集。CFrameWnd::OnDDEExecute已變更為 (CWnd*, HANDLE),而不再是 (WPARAM, LPARAM),因此可在訊息映射中使用新的 ON_WM_DDE_EXECUTE 巨集。CFrameWnd::OnDDETerminate已變更為 (CWnd*) 作為參數,而不再是 (WPARAM, LPARAM),所以可在訊息映射中使用新的 ON_WM_DDE_TERMINATE 宏。CMFCMaskedEdit::OnCut已變更為無參數模式,而不再使用 (WPARAM, LPARAM),以便在訊息映射中使用新的 ON_WM_CUT 巨集。CMFCMaskedEdit::OnClear已變更為不使用任何參數,而不是 (WPARAM, LPARAM),因此可以在消息映射中使用新的 ON_WM_CLEAR 巨集。CMFCMaskedEdit::OnPaste已更改為不使用任何參數,而非 (WPARAM, LPARAM),以便在消息映射中使用新的 ON_WM_PASTE 巨集。
MFC 標頭中的
#ifdef指示詞已移除。 已移除 MFC 標頭檔中許多與不支援之 Windows 版本相關的#ifdef指示詞 (WINVER < 0x0501)。ATL DLL (atl120.dll) 已移除。 現在提供的 ATL 為標頭檔和靜態程式庫 (atls.lib)。
Atlsd.lib、atlsn.lib 和 atlsnd.lib 已移除。 Atls.lib 不再具有字元集相依性或偵錯/發行專屬的程式碼。 由於對於 Unicode/ANSI 和偵錯/發行,它的運作方式相同,因此只需要一個版本的程式庫。
ATL/MFC 追蹤工具會隨 ATL DLL 移除,追蹤機制也有所簡化。
CTraceCategory建構函式現在會接受一個參數(即分類名稱),而 TRACE 巨集會呼叫 CRT 偵錯報告函式。
Visual Studio 2012 的重大變更
編譯器
/Yl編譯器選項已變更。 編譯器預設會使用此選項,在某些情況下,可能會導致 LNK2011 錯誤。 如需詳細資訊,請參閱 /Yl (Inject PCH Reference for Debug Library) (/Yl (插入偵錯程式庫的 PCH 參考))。在使用
/clr編譯的程式碼中,enum類別關鍵字會定義 C++11 列舉,而非通用語言執行平台 (CLR) 列舉。 若要定義 CLR 列舉,您必須明確設定其存取層級。使用範本關鍵字,明確釐清相依名稱 (符合 C++ 語言標準規範)。 在下列範例中,突顯的範本關鍵字是消解模稜兩可情況的必備項目。 如需詳細資訊,請參閱 Name Resolution for Dependent Types (相依類型的名稱解析)。
template < typename X = "", typename = "" AY = ""> struct Container { typedef typename AY::template Rebind< X> ::Other AX; };類型 float 的常數運算式已不可再用為範本引數,如下列範例所示。
template<float n=3.14> struct B {}; // error C2993: 'float': illegal type for non-type template parameter 'n'使用
/GS命令列選項編譯的程式碼若具有偏差一 (off-by-one) 弱點,可能於執行時導致進程終止,如下列虛擬程式碼範例所示。char buf[MAX]; int cch; ManipulateString(buf, &cch); // ... buf[cch] = '\0'; // if cch >= MAX, process will terminatex86 組建的預設結構已變更為 SSE2;因此,編譯器可能會發出 SSE 指令,並會使用 XMM 暫存器執行浮點計算。 若要還原成先前的行為,可使用
/arch:IA32編譯器旗標將架構指定為 IA32。編譯器可能會發出下列警告:編譯器警告 (層級 4) C4703及 C4701,但先前不會如此。 編譯器會加強對使用指標類型未經初始化區域變數的檢查。
如有指定新連結器旗標
/HIGHENTROPYVA,Windows 8 通常會導致記憶體配置傳回 64 位元的位址。 在 Windows 8 之前,這類配置更常返回小於 2 GB 的位址。這項變更可能會暴露現有程式碼中的指標截斷錯誤。 預設情況下,此開關是開的。 若要停用此行為,請指定/HIGHENTROPYVA:NO。針對受控組建,受控編譯器 (Visual Basic/C#) 也支援
/HIGHENTROPYVA。 但在此情況下,/HIGHENTROPYVAswitch預設為關閉。
IDE
- 雖然我們建議您不要使用 C++/CLI 建立 Windows Form 應用程式,但仍能維護現有的 C++ /CLI 應用程式。 若您已經建立了 Windows Form 應用程式或任何其他 .NET UI 應用程式,請使用 C# 或 Visual Basic。 唯有在互通性的考量下,才使用 C++/CLI。
平行模式程式庫與並行執行階段程式庫
SchedulerType 的 UmsThreadDefault 列舉型別已被棄用。 指定 UmsThreadDefault 會產生淘汰的警告,並會在內部對應回 ThreadScheduler。
標準程式庫
由於 C++98/03 與 C++11 標準之間的重大變更,在 Visual Studio 2012 中的 Visual C++ 內使用明確範本引數來呼叫
make_pair()通常無法編譯,正如make_pair<int, int>(x, y)。 解決方案是一律呼叫make_pair(),而不要明確指定範本引數 (例如make_pair(x, y))。 提供明確的範本引數會導致函式失效。 若您需要精確控制產生的類型,請改為使用pair而非make_pair,如同pair<short, short>(int1, int2)。C++98/03 與 C++11 標準之間的另一項中斷性變更︰當 A 可隱含轉換成 B 且 B 可隱含轉換成 C,但 A 不可隱含轉換成 C 時,C++98/03 與 Visual Studio 2010 允許將
pair<A, X>轉換 (隱含或明確) 成pair<C, X>。 (另一個類型 X 不是此處的重點,且不是配對中第一種類型的專用類型)。Visual Studio 2012 中的 C++ 編譯器偵測到 A 並未隱含表示可轉換成 C,所以會從多載解析中移除配對轉換。 這項變更對許多狀況而言有益。 例如,多載func(const pair<int, int>&)和func(const pair<string, string>&),以及使用func()呼叫pair<const char *, const char *>時,便會使用這項變更進行編譯。 但此變更會破壞需要積極執行 pair 轉換的程式碼。 一般可以藉由明確執行轉換的其中一部分來修正這類程式碼,例如將make_pair(static_cast<B>(a), x)傳遞給需要pair<C, X>的函式。Visual Studio 2010 採用前置處理器機制透過多載和特化來自動生成 variadic 模板(例如
make_shared<T>(arg1, arg2, argN))的實例,限制為最多 10 個參數。 在 Visual Studio 2012 中,此限制縮減為五個引數,以改善大多數使用者的編譯時間及編譯器記憶體耗用量。 但您可以藉由將 _VARIADIC_MAX 明確定義為 10 來將整個專案設定成先前的限制。當包含 C++ 標準程式庫標頭時,C++11 17.6.4.3.1 [macro.names]/2 禁止進行關鍵字的巨集替換。 當標頭檔案偵測到經巨集替換的關鍵字時,現在會產生編譯錯誤。 (定義 _ALLOW_KEYWORD_MACROS 可允許編譯這類程式碼,但極力建議不要如此定義)。作為例外狀況,預設允許使用巨集形式的
new,因為標頭使用#pragma push_macro("new")/#undef new/#pragma pop_macro("new")進行全面自我防禦。 定義 _ENFORCE_BAN_OF_MACRO_NEW 完全如其名稱所示。為實作各種最佳化及偵錯檢查,C++ 標準程式庫實作是刻意中斷了各版 Visual Studio (2005、2008、2010、2012) 之間的二進位相容性。 當使用 C++ 標準程式庫時,這會導致無法將物件檔案與使用不同版本編譯的靜態程式庫混合成一個二進位檔 (EXE 或 DLL),且也無法在使用不同版本編譯的二進位檔之間傳遞 C++ 標準程式庫物件。 混合物件檔案與靜態程式庫 (使用由 Visual Studio 2010 編譯之 C++ 標準程式庫與使用 Visual Studio 2012 之 C++ 編譯器編譯的 C++ 標準程式庫) 會發出有關 _MSC_VER 不符的連結器錯誤,其中 _MSC_VER 是包含編譯器主要版本 (Visual Studio 2012 的 Visual C++ 為 1700) 的巨集。 這項檢查無法偵測 DLL 混合,且無法偵測包含 Visual Studio 2008 及較舊版本的混合。
除了偵測 _ITERATOR_DEBUG_LEVEL 不符的情況 (實作於 Visual Studio 2010) 之外,Visual Studio 2012 的 C++ 編譯器還會偵測執行階段程式庫不符的錯誤。 當編譯器選項
/MT(靜態發行)、/MTd(靜態偵錯)、/MD(動態發行) 和/MDd(動態偵錯) 混合時,就會發生這些不相符的情況。針對
std::unordered_map和stdext::hash_map容器系列,先前可以使用operator<()、operator>()、operator<=()和operator>=(),雖然其實作並不是很有用。 因此 Visual Studio 2012 的 Visual C++ 移除了這些非標準運算子。 此外,operator==()系列的operator!=()和std::unordered_map實作已延伸至涵蓋stdext::hash_map系列。 (建議您避免在新的程式碼中使用stdext::hash_map系列。)C++11 22.4.1.4 [locale.codecvt] 指定
codecvt::length()和codecvt::do_length()應接受可修改的stateT&參數,但 Visual Studio 2010 接受的參數為const stateT&。 而 Visual Studio 2012 的 C++ 編譯器則因為遵循標準而接受stateT&。 對於想要覆寫虛擬函式do_length()的使用者而言,此差異相當重大。
CRT
new 與 malloc() 中使用的 C 執行階段 (CRT) 堆積已不再是私用。 CRT 現在會使用處理序堆積。 這表示堆積不會在 DLL 卸載之後終結,因此,靜態連結到 CRT 的 DLL 必須在卸載之前先清除 DLL 程式碼所配置的記憶體。
iscsymf()函式對負數值進行斷言。threadlocaleinfostruct結構已變更為可接受地區設定函式的變更。具有對應內建函式的 CRT 函式 (例如
memxxx()、strxxx()) 已從 intrin.h 中移除。 若這些函式中只包含 intrin.h,現在也必須加入相對應的 CRT 標頭。
MFC 和 ATL
已移除融合支援 (afxcomctl32.h);因此,所有在
<afxcomctl32.h>中定義的方法也會一併移除。<afxcomctl32.h>和<afxcomctl32.inl>標頭檔已刪除。已將
CDockablePane::RemoveFromDefaultPaneDividier的名稱變更為CDockablePane::RemoveFromDefaultPaneDivider。已將
CFileDialog::SetDefExt的簽章變更為使用 LPCTSTR,因此會影響 Unicode 組建。移除了過時的 ATL 追蹤分類。
已變更
CBasePane::MoveWindow的簽章,使其接受const CRect。已變更
CMFCEditBrowseCtrl::EnableBrowseButton的簽章。已從
m_fntTabs移除m_fntTabsBold和CMFCBaseTabCtrl。已將參數新增至
CMFCRibbonStatusBarPane建構函式。 (這是預設參數,所以不是來源中斷的問題)。已將參數新增至
CMFCRibbonCommandsListBox建構函式。 (這是預設參數,所以不是來源中斷的問題)。已移除
AFXTrackMouseAPI (及相關計時器處理序)。 請改為使用 Win32TrackMouseEventAPI。已將參數新增至
CFolderPickerDialog建構函式。 (這是預設參數,所以不是來源中斷的問題)。CFileStatus結構的大小已變更:m_attribute成員已從 BYTE 變更為 DWORD (以符合GetFileAttributes傳回的值)。CRichEditCtrl和CRichEditView會在 Unicode 組建中使用 MSFTEDIT_CLASS (RichEdit 4.1 控制) 而非 RICHEDIT_CLASS (RichEdit 3.0 控制)。已移除
AFX_GLOBAL_DATA::IsWindowsThemingDrawParentBackground,因為它在 Windows Vista、Windows 7 和 Windows 8 上一律為 TRUE。已移除
AFX_GLOBAL_DATA::IsWindowsLayerSupportAvailable,因為它在 Windows Vista、Windows 7 和 Windows 8 上一律為 TRUE。已移除
AFX_GLOBAL_DATA::DwmExtendFrameIntoClientArea。 直接在 Windows Vista、Windows 7 及 Windows 8 上呼叫 Windows API。已移除
AFX_GLOBAL_DATA::DwmDefWindowProc。 直接在 Windows Vista、Windows 7 及 Windows 8 上呼叫 Windows API。已將
AFX_GLOBAL_DATA::DwmIsCompositionEnabled重新命名為IsDwmCompositionEnabled以避免名稱衝突。變更了許多 MFC 內部計時器的識別碼,並將定義移至 afxres.h (AFX_TIMER_ID_ *)。
已將
OnExitSizeMove方法的簽章變更為與 ON_WM_EXITSIZEMOVE 巨集一致:CFrameWndExCMDIFrameWndExCPaneFrameWnd
已將
OnDWMCompositionChanged的名稱和簽章變更為與 ON_WM_DWMCOMPOSITIONCHANGED 巨集一致:CFrameWndExCMDIFrameWndExCPaneFrameWnd
已將
OnMouseLeave方法的簽章變更為與 ON_WM_MOUSELEAVE 巨集一致:CMFCCaptionBarCMFCColorBarCMFCHeaderCtrlCMFCProperySheetListBoxCMFCRibbonBarCMFCRibbonPanelMenuBarCMFCRibbonRichEditCtrlCMFCSpinButtonCtrlCMFCToolBar替換此文字CMFCToolBarComboBoxEditCMFCToolBarEditCtrlCMFCAutoHideBar
已將
OnPowerBroadcast的簽章變更,使其與 ON_WM_POWERBROADCAST 巨集一致:CFrameWndExCMDIFrameWndEx
已將
OnStyleChanged的簽章變更為與 ON_WM_STYLECHANGED 巨集一致:CMFCListCtrlCMFCStatusBar
已將內部方法
FontFamalyProcFonts重新命名為FontFamilyProcFonts。已移除多個全域靜態
CString物件,以解決某些狀況中的記憶體流失問題 (由 #defines 取代),同時也移除了下列類別成員變數:CKeyBoardManager::m_strDelimiterCMFCPropertyGridProperty::m_strFormatCharCMFCPropertyGridProperty::m_strFormatShortCMFCPropertyGridProperty::m_strFormatLongCMFCPropertyGridProperty::m_strFormatUShortCMFCPropertyGridProperty::m_strFormatULongCMFCPropertyGridProperty::m_strFormatFloatCMFCPropertyGridProperty::m_strFormatDoubleCMFCToolBarImages::m_strPngResTypeCMFCPropertyGridProperty::m_strFormat
已變更
CKeyboardManager::ShowAllAccelerators的簽章,並移除了快速鍵分隔符號參數。已新增
CPropertyPage::GetParentSheet,並在CPropertyPage類別中改為呼叫它而非GetParent,以取得正確的父工作表視窗,而該視窗可能是CPropertyPage的父視窗或更高層的父視窗。 您可能必須變更您的程式碼,才能呼叫GetParentSheet而不是GetParent。修正了 ATLBASE.H 中不對稱的 #pragma warning(push),導致警告被不正確地停用。 這些警告在 ATLBASE.H 完成剖析之後,現在已經正確啟用。
移動了與 D2D 相關的方法從 AFX_GLOBAL_DATA 至 _AFX_D2D_STATE 中:
GetDirectD2dFactoryGetWriteFactoryGetWICFactoryInitD2DReleaseD2DRefsIsD2DInitializedD2D1MakeRotateMatrix而不是呼叫
afxGlobalData.IsD2DInitialized,而應改為呼叫AfxGetD2DState->IsD2DInitialized。
移除了 \atlmfc\include\ 資料中過時的 ATL*.CPP 檔案。
已將
afxGlobalData初始化移至隨選,而不是在 CRT 初始化階段,以滿足DLLMain需求。已將
RemoveButtonByIndex方法新增至CMFCOutlookBarPane類別。已將
CMFCCmdUsageCount::IsFreqeuntlyUsedCmd修正為IsFrequentlyUsedCmd。已將
RestoreOriginalstate的數個執行個體修正為RestoreOriginalState (CMFCToolBar, CMFCMenuBar, CMFCOutlookBarPane)。已從
CDockablePane移除未使用的方法:SetCaptionStyle、IsDrawCaption、IsHideDisabledButtons、GetRecentSiblingPaneInfo和CanAdjustLayout。已移除
CDockablePane靜態成員變數m_bCaptionText及m_bHideDisabledButtons。已將覆寫
DeleteString方法新增至CMFCFontComboBox。已從
CPane移除未使用的方法:GetMinLength和IsLastPaneOnLastRow。已將
CPane::GetDockSiteRow(CDockingPanesRow *)重新命名為CPane::SetDockSiteRow。
Visual Studio 2010 的重大變更
編譯器
auto關鍵字具有新的預設意義。 因為舊的意義甚少使用,所以這項變更對於大多數的應用程式沒有影響。目前引進了新的
static_assert關鍵字,如果您的程式碼中已經有該名稱的識別碼,則其將引發名稱衝突。新的 lambda 標記法不支援在 IDL uuid 屬性中編寫未加引號的 GUID。
.NET Framework 4 引進了損毀狀態例外狀況概念,亦即在此例外狀況中,處理序會處於無法復原的損毀狀態。 根據預設,您無法捕捉損毀狀態例外狀況,即使使用可捕捉所有其他例外狀況的 /EHa 編譯器選項也無法達成此目的。 若要明確捕捉損毀狀態例外,請使用 __try-__except 陳述式。 或是將 [HandledProcessCorruptedStateExceptions] 屬性應用於函式,以啟用捕捉損毀狀態例外狀況。 此變更主要會影響可能需要擷取損毀狀態例外狀況的系統程式設計人員。 這八個例外狀況包括:STATUS_ACCESS_VIOLATION、STATUS_STACK_OVERFLOW、EXCEPTION_ILLEGAL_INSTRUCTION、EXCEPTION_IN_PAGE_ERROR、EXCEPTION_INVALID_DISPOSITION、EXCEPTION_NONCONTINUABLE_EXCEPTION、EXCEPTION_PRIV_INSTRUCTION、STATUS_UNWIND_CONSOLIDATE。 如需這些例外狀況的詳細資訊,請參閱 GetExceptionCode 巨集。
相較於舊版,修改後的
/GS編譯器選項會更密集地監視緩衝區滿溢狀況。 此版可能會在堆疊中插入額外安全性檢查,因而造成效能降低。 使用新的__declspec(safebuffers)關鍵字可指示編譯器,針對特定函式不要插入安全性檢查。若同時使用
/GL(整個程式最佳化) 與/clr(通用語言執行平台編譯) 編譯器選項進行編譯,將會忽略/GL選項。 進行此變更是因為編譯選項的組合所能帶來的好處有限。 此變更讓建置的效能獲得提升。Visual Studio 2010 預設會停用三字母組合的支援。 若要啟用三併詞的支援,可使用
/Zc:trigraphs編譯器選項。 三字符組由兩個連續的問號 ("??") 加上一個獨特的第三個字元組成。 編譯器會以對應的標點字元取代三字符組。 例如,編譯器會將??=三併詞取代為 '#' 字元。 若在 C 原始程式檔中使用的字元集缺少某些標點符號的方便圖形表示,您可以使用三字組來代替。連結器已不再支援 Windows 98 的最佳化。 若指定
/OPT或/OPT:WIN98,/OPT:NOWIN98(最佳化) 選項會產生編譯時期錯誤。由 RuntimeLibrary 與 DebugInformationFormat 建置系統屬性指定的預設編譯器選項已有變更。 這些屬性預設會在 Visual C++ 第 7.0 版到 10.0 版所建立的專案中指定。 若您移轉了 Visual C++ 6.0 所建立的專案,請考慮是否要指定這些屬性的值。
在 Visual Studio 2010 中,RuntimeLibrary = MultiThreaded (
/MD) 且 DebugInformationFormat = ProgramDatabase (/Zi)。 在 Visual C++ 9.0 中,RuntimeLibrary = MultiThreaded (/MT) 且 DebugInformationFormat = Disabled。
CLR
- Microsoft C# 與 Visual Basic 編譯器現在可以產生不含主要 interop 程式集的組件 (no-PIA)。 非 PIA 組件不需要部署相關的主要 interop 組件 (PIA),就能使用 COM 類型。 在使用由 Visual C# 或 Visual Basic 所生成的非 PIA 組件時,您必須在編譯命令中先引用 PIA 組件,然後才可以引用使用該程式庫的非 PIA 組件。
Visual C++ 專案與 MSBuild
Visual Studio C++ 專案現在使用 MSBuild 工具。 因此,專案檔會使用新的 XML 檔案格式與 .vcxproj 檔案後置字元。 Visual Studio 2010 會自動將舊版 Visual Studio 專案檔轉換成新的檔案格式。 現有的專案若是使用舊版的建置工具 VCBUILD.exe 或專案檔案後置字元.vcproj,便會受到影響。
在早期版本中,Visual C++ 支援延遲求值屬性表單。 例如,父屬性工作表可以匯入子屬性工作表,然後父屬性工作表可以使用子屬性工作表中定義的變數來定義其他變數。 延遲評估機制讓父項在子屬性工作表匯入之前就能使用子項變數。 在 Visual Studio 2010 中,因為 MSBuild 只支援早期評估,所以無法在定義專案工作表變數之前先行使用。
IDE
此應用程式的終止對話方塊不會再結束應用程式。 在先前版本中,當
abort()或terminate()函式關閉應用程式的零售版本時,C 執行階段程式庫會在主控台視窗或對話方塊中顯示應用程式終止訊息。 此訊息只顯示部分:「此應用程式要求執行階段以異常方式將其終止。 如需詳細資訊,請連絡應用程式支援小組。」因為 Windows 會接著顯示目前的終止處理常式 (通常為 Windows 錯誤報告 (Dr.Watson) 對話方塊或 Visual Studio 偵錯工具),讓此應用程式終止訊息顯得多餘。 自 Visual Studio 2010 起,C 執行階段程式庫不再顯示此訊息。 此外,此執行階段會造成應用程式無法在偵錯工具啟動前結束。 僅當您依賴於應用程式終止訊息的先前行為時,這項中斷性變更才會對您產生影響。僅限 Visual Studio 2010:IntelliSense 不適用於 C++/CLI 程式碼或屬性;[尋找所有參考] 不適用於本機變數;程式碼模型不會從匯入的組件擷取類型名稱,也不會將類型解析成完整名稱。
程式庫
SafeInt 類別包含在 Visual C++ 中,而且不再需要個個別下載。 只有當您開發的類別名稱也叫 "SafeInt" 時,這項破壞性變更才會造成重大影響。
程式庫部署模型不再使用訊清單來尋找特定版本的動態連結程式庫。 取而代之地是在每個動態連結程式庫名稱中加入其版本號碼,讓您可以使用該名稱尋找程式庫。
在舊版的 Visual Studio 中,您可以重建執行階段程式庫。 在 Visual Studio 2010 中,您已無法建置自己的 C 執行階段程式庫檔案。
標準程式庫
其他許多標頭檔已不再自動包含
<iterator>標頭。 相反地,如果您需要對標頭中所定義獨立迭代器的支援,請明確包含該標頭。 現有專案若是使用舊版建置工具 VCBUILD.exe 或專案檔案後置字元 .vcproj.iterator,便會受到影響。在
<algorithm>標頭中,移除checked_*和unchecked_*函式。 然後,在<iterator>> 標頭中,移除checked_iterator類別,且已新增unchecked_array_iterator類別。已移除
CComPtr::CComPtr(int)建構函式。 該建構函式允許從 NULL 巨集建構CComPtr物件,但這並非必要,而且它也允許從非零整數產生的無意義建構。CComPtr仍然可從 NULL (定義為 0) 建構,但若是從常值 0 以外的整數建構則會失敗。 請改用nullptr。下列
ctype成員函式已移除:ctype::_Do_narrow_s、ctype::_Do_widen_s、ctype::_narrow_s、ctype::_widen_s。 若應用程式使用下列其中一個成員函式,您必須使用對應的不安全版本取代:ctype::do_narrow、ctype::do_widen、ctype::narrow、ctype::widen。
CRT、MFC 及 ATL 程式庫
使用者已無法再建置 CRT、MFC 及 ATL 程式庫。 例如,未提供任何適當的 NMAKE 檔案。 但使用者仍能存取這些程式庫的原始程式碼。 且描述 Microsoft 使用 MSBuild 選項來建置這些程式庫時的文件,可能會張貼在 Visual C++ 小組部落格中。
IA64 的 MFC 支援已移除。 仍然提供 IA64 上的 CRT 和 ATL 支援。
序數在 MFC 模組定義 (.def) 檔案中已無法再重複使用。 此變更表示次要版本之間的序數將保持一致,而且 Service Pack 及快速修復工程版本的二進位相容性將有所改進。
已將新虛擬函式新增至
CDocTemplate類別。 這個新的虛擬函式是 CDocTemplate 類別。OpenDocumentFile的先前版本有兩個參數。 新版則具有三個參數。 為了支援重新啟動管理員,任何從CDocTemplate衍生的類別,都必須實作擁有三個參數的版本。 新的參數為bAddToMRU。
巨集與環境變數
- 環境變數 __MSVCRT_HEAP_SELECT 已不再支援。 此環境變數已予移除,而且不提供任何取代項目。
Microsoft Macro 組合程式參考
- Microsoft Macro Assembler 參考編譯器移除了幾個指令。 移除的指示詞為
.186、.286、.286P、.287、.8086、.8087及.NO87。
Visual Studio 2008 的重大變更
編譯器
不再支援 Windows 95、Windows 98、Windows ME 及 Windows NT 平台。 這些作業系統已從目標平台清單中移除。
此編譯器已不再支援多個直接關聯到 ATL 伺服器的屬性。 以下是不再支援的屬性︰
perf_counter
perf_object
perfmon
請求處理程式
SOAP處理程序
SOAP標頭
SOAP 方法
標籤名稱
Visual Studio C++ 專案
從舊版的 Visual Studio 升級專案時,可能須修改 WINVER 及 _WIN32_WINNT 巨集,使其大於或等於 0x0500。
自 Visual Studio 2008 起,[新增專案精靈] 不再提供建立 C++ SQL Server 專案的選項。 使用舊版 Visual Studio 建立的 SQL Server 專案仍可正常地編譯及運作。
Windows API 標頭檔 Winable.h 已移除。 改為加入 Winuser.h。
Windows API 程式庫 Rpcndr.lib 已移除。 改用 rpcrt4.lib 進行鏈接。
CRT
對 Windows 95、Windows 98、Windows Millennium Edition 及 Windows NT 4.0 的支援已移除。
以下是移除的全域變數︰
_osplatform
_osver
_winmajor
_winminor
_winver
下列函式已移除。 請改為使用 Windows API 函式
GetVersion或GetVersionEx:_get_osplatform
_get_osver
_get_winmajor
_get_winminor
_get_winver
SAL 註釋的語法已變更。 如需詳細資訊,請參閱 SAL 註釋。
IEEE 篩選現在支援 SSE 4.1 指令集。 如需詳細資訊,請參閱
_fpieee_flt。Visual Studio 隨附的 C 執行階段程式庫已不再使用系統 DLL msvcrt.dll。
標準程式庫
對 Windows 95、Windows 98、Windows Millennium Edition 及 Windows NT 4.0 的支援已移除。
在定義 _HAS_ITERATOR_DEBUGGING (Visual Studio 2010 之後由_ITERATOR_DEBUG_LEVEL 取代) 的偵錯模式中編譯時,應用程式現在已可斷定迭代器何時須嘗試遞增或遞減過去的基礎容器界限。
現在 stack 類別的成員變數 c 被宣告為 protected。 此成員變數先前宣告為公用。
money_get::do_get的行為已變更。 以前,當剖析的貨幣金額小數位數多於frac_digits所需的小數位數時,do_get通常會全部丟失不必要的小數位數。 現在,do_get會在取用最多frac_digits個字元之後停止剖析。
ATL
若不相依於 CRT,即無法建置 ATL。 在舊版 Visual Studio 中,您可以使用 #define ATL_MIN_CRT 讓 ATL 專案使用最少的 CRT。 在 Visual Studio 2008 中,所有 ATL 專案都對 CRT 的依賴性最低,無論是否定義 ATL_MIN_CRT。
ATL Server 程式碼基底已發行到 CodePlex 成為共用原始碼專案,且不再隨 Visual Studio 一起安裝。 atlenc.h 中的資料編碼與解碼類別,以及 atlutil.h 與 atlpath.h 中的公用程式函式與類別皆予保留,現在是 ATL 程式庫的一部分。 有幾個與 ATL Server 相關聯的檔案已不再屬於 Visual Studio 的一部分。
有些函式已不再包含在 DLL 中, 它們仍然位於匯入程式庫。 這不會影響靜態使用函式的程式碼。 只有動態使用這些函式的程式碼才會受到影響。
基於安全考量,PROP_ENTRY 和 PROP_ENTRY_EX 巨集已被淘汰,並已被 PROP_ENTRY_TYPE 和 PROP_ENTRY_TYPE_EX 巨集取代。
ATL/MFC 共用類別
若不相依於 CRT,即無法建置 ATL。 在舊版 Visual Studio 中,您可以使用
#define ATL_MIN_CRT來最小化對 CRT 的依賴於 ATL 專案。 在 Visual Studio 2008 中,所有 ATL 專案都對 CRT 的依賴性最低,無論是否定義 ATL_MIN_CRT。ATL Server 程式碼基底已發行到 CodePlex 成為共用原始碼專案,且不再隨 Visual Studio 一起安裝。 atlenc.h 中的資料編碼與解碼類別,以及 atlutil.h 與 atlpath.h 中的公用程式函式與類別皆予保留,現在是 ATL 程式庫的一部分。 有幾個與 ATL Server 相關聯的檔案已不再屬於 Visual Studio 的一部分。
有些函式已不再包含在 DLL 中, 他們仍然位於匯入庫。 這不會影響靜態使用函式的程式碼。 只有動態使用這些函式的程式碼才會受到影響。
MFC
CTime類別︰CTime類別現在接受自西元 1/1/1900 (而非西元 1/1/1970) 起的日期。MFC 對話方塊中控制項的索引標籤︰當在索引標籤順序中插入 MFC ActiveX 控制項時,會影響 MFC 對話方塊中多個控制項的正確索引標籤順序。 此變更修正了這個問題。
例如建立具有 ActiveX 控制項與幾個編輯控制項的 MFC 對話方塊應用程式。 將 ActiveX 控制項置於編輯控制項的索引標籤定位中間。 啟動應用程式,點擊一個索引標籤順序位於 ActiveX 控制項之後的編輯控制項,然後按下 Tab 鍵。在此變更之前,焦點會移到緊接在 ActiveX 控制項之後的編輯控制項,而不是索引標籤順序中的下一個編輯控制項。
CFileDialog類別:CFileDialog類別的自訂範本無法自動移植到 Windows Vista。 它們仍可使用,但沒有額外的功能,也不是 Windows Vista 樣式對話框的外觀。CWnd類別和CFrameWnd類別:已移除CWnd::GetMenuBarInfo方法。CFrameWnd::GetMenuBarInfo方法現在為非虛擬方法。 如需詳細資訊,請參閱 Windows SDK 中的<GetMenuBarInfo 函式>。MFC ISAPI 支援︰MFC 已不再支援透過網際網路伺服器應用程式開發介面 (ISAPI) 建置應用程式。 若要建置 ISAPI 應用程式,請直接呼叫 ISAPI 延伸模組。
已不建議使用的 ANSI API:有幾個 MFC 方法的 ANSI 版本被標示為不建議使用。 在您後續的應用程式中,須改用這些方法的 Unicode 版本。 如需詳細資訊,請參閱<Windows Vista 通用控制項的建置需求>。
Visual Studio 2005 的重大變更
CRT
許多函式已標示為即將淘汰。 請參閱「已棄用的 CRT 函式」。
許多函式現在會驗證其參數,並在提供的參數無效時停止執行。 這項驗證可中斷程式碼傳遞無效的參數,並利用函式忽略這些參數或只是傳回錯誤碼。 請參閱<參數驗證>。
現已使用檔案描述元值 -2 來指示
stdout和stderr無法進行輸出,例如在沒有主控台視窗的 Windows 應用程式中。 先前使用的值為 -1。 如需詳細資訊,請參閱 _fileno。已移除單一執行緒的 CRT 程式庫 (libc.lib 與 libcd.lib)。 請使用多執行緒的 CRT 程式庫。 已不再支援
/ML編譯器旗標。 針對多執行緒程式碼與單一執行緒程式碼的效能可能會出現大幅差異的情況新增了一些非鎖定版本的功能。pow, double pow(int, int) 的多載已移除,以更加貼近標準的要求。
有鑑於 %n 格式指定名稱本身就不安全,所以所有 printf 系列函式預設都不再提供此支援。 如果出現 %n,預設行為是叫用無效參數處理常式。 若要啟用 %n 支援,請使用
_set_printf_count_output(另請參閱_get_printf_count_output)。對於帶正負號的零,
sprintf現在會列印負號。swprintf已變更為符合標準,所以現在會需要大小參數。 沒有大小參數的swprintf形式已經被歸類為不推薦。已移除
_set_security_error_handler。 移除所有對該函數的呼叫;使用預設處理常式來處理安全性錯誤是一種更安全的方法。time_t現在是 64 位元值 (除非定義 _USE_32BIT_TIME_T)。_spawn、_wspawn函式現在會依照 C 標準的規定,在成功時保留原有的errno。RTC 現在預設會使用寬字元。
浮點控制字支援函式已過時,並不再供使用
/CLR或/CLR:PURE編譯的應用程式使用。 受影響的函式包括_clear87、_clearfp、_control87、_controlfp、_fpreset、_status87、_statusfp。 您可以藉由定義 _CRT_MANAGED_FP_NO_DEPRECATE 來停用已標示為即將淘汰的警告,但在 Managed 程式碼中使用這些函式可能會造成無法預期且不受支援的結果。有些函式現在會傳回 const 指標。 舊的 非 const 行為可藉由定義 _CONST_RETURN 重新啟用。 受影響的函式包括:
memchr、wmemchr
strchr、wcschr、_mbschr、_mbschr_l
strpbrk、wcspbrk、_mbspbrk、_mbspbrk_l
strrchr、wcsrchr、_mbsrchr、_mbsrchr_l
strstr、wcsstr、_mbsstr、_mbsstr_l
當連結到 Setargv.obj 或 Wsetargv.obj 時,即使將萬用字元括上雙引號,也無法在命令列中抑制其展開。 如需詳細資訊,請參閱 Expanding Wildcard Arguments (展開萬用字元引數)。
標準程式庫 (2005)
例外狀況類別 (位於
<exception>標頭中) 已移至std命名空間。 在舊版中,此類別位於全域命名空間。 若要解決指出找不到例外狀況類別的任何錯誤,請在您的程式碼中新增下列 using 陳述式︰using namespace std;當呼叫
valarray::resize()時,valarray的內容會遺失並會由預設值取代。resize()方法主要用於重新初始化valarray,而不是像 vector 一樣動態增加。偵錯迭代器︰使用偵錯版本之 C 執行階段程式庫建置的應用程式若是不正確地使用迭代器,可能會在執行階段看到判斷提示。 若要停用這些判斷提示,您必須將 _HAS_ITERATOR_DEBUGGING (Visual Studio 2010 之後由
_ITERATOR_DEBUG_LEVEL所取代) 定義為 0。 如需詳細資訊,請參閱偵錯迭代器支援。
Visual C++ .NET 2003 的重大變更
編譯器
定義的前置處理器指令現在必須用右括號 (C2004)。
明確特化已無法再從主範本中找到模板參數 (編譯器錯誤 C2146)。
受保護的成員 (n) 只能透過從其所在的類別(A)繼承的類別(B)的成員函式進行存取,此時 (n) 是類別(A)的成員。(編譯器錯誤 C2247)。
改進後的編譯器可存取性檢查現在可以偵測到無法存取的基底類別 (編譯器錯誤 C2248)。
若解構函式及/或複製建構函式無法存取,就無法捕捉例外狀況 (C2316)。
函式指標已不再有任何預設引數 (編譯器錯誤 C2383)。
靜態資料成員無法再透過衍生類別初始化 (編譯器錯誤 C2477)。
該標準不允許初始化
typedef,且現在會產生編譯器錯誤 (編譯器錯誤 C2513)。bool現在是正確的類型 (編譯器錯誤 C2632)。UDC 現在可以創造重載運算子的模稜兩可性 (C2666)。
多個表示式現在會被視為有效的 null 指標常數 (編譯器錯誤 C2668)。
過去編譯器可以隱含表示的 template<> 現在必須明確存在 (編譯器錯誤 C2768)。
如果函式已透過範本類別特化而被明確特化,那麼在類別外部再對該成員函式進行明確特化是不合法的 (編譯器錯誤 C2910)。
浮動點非類型範本參數不得再使用 (編譯器錯誤 C2993)。
類別範本不得作為範本類型引數使用 (C3206)。
"Friend 函式名稱不再引入到所屬的命名空間中 (編譯器錯誤 C3767)。"
編譯器將不再接受宏中有任何額外的逗點 (C4002)。
形如 () 的初始化子建構的 POD 類型物件將會被預設初始化 (C4345)。
若要將相依名稱視為類型,現在需要 typename (編譯器警告 (層級 1) C4346)。
先前被誤認為樣板特製化的函式現今不再被如此視為 (C4347)。
靜態資料成員無法透過衍生類別初始化 (C4356)。
必須先定義類別範本特製化,才能在傳回型別中加以使用 (編譯器警告 (層級 3) C4686)。
編譯器現在會回報無法存取的程式碼 (C4702)。