Visual Studio 2022 中的 C++ 一致性改善、行為變更和錯誤修正
Visual Studio 中的 Microsoft C/C++ 會在每個版本中進行一致性改善和 Bug 修正。 本文列出主要版本、版本的重大改善。 若要直接跳至特定版本的變更,請使用 本文 連結。
本檔列出 Visual Studio 2022 中的變更。
如需 Visual Studio 2019 中的變更,請參閱 Visual Studio 2019 中的 C++ 一致性改善。
如需 Visual Studio 2017 中的變更,請參閱 Visual Studio 2017 中的 C++ 一致性改善。
如需舊版的變更,請參閱 Visual C++ 新功能 2003 到 2015。
Visual Studio 2022 17.9 版中的一致性改善
Visual Studio 2022 17.9 版包含 Microsoft C/C++ 編譯程式中的下列一致性改進、錯誤修正和行為變更。
如需對標準範本連結庫所做的變更更廣泛摘要,請參閱 STL Changelog VS 2022 17.9。
在 _Alignas
C 中結構化型別上的 應用程式
在 Visual Studio 2022 17.9 版之前的 Visual C++ 版本中,在宣告中出現在結構類型旁邊時 _Alignas
,它未根據 ISO-C 標準正確套用。 例如:
// compile with /std:c17
#include <stddef.h>
struct Outer
{
_Alignas(32) struct Inner { int i; } member1;
struct Inner member2;
};
static_assert(offsetof(struct Outer, member2)==4, "incorrect alignment");
根據 ISO-C 標準,此程式代碼應該在沒有發出診斷的情況下 static_assert
進行編譯。 指示 _Alignas
詞只適用於成員變數 member1
。 它不得變更的 struct Inner
對齊方式。 不過,在Visual Studio 17.9.1版之前,發出診斷「不正確的對齊方式」。 編譯程式在 member2
內 struct Outer
對齊 32 位元組位移。
修正這是二進位中斷性變更,因此當套用此行為變更時,就會發出警告。 針對上述程式代碼 Warning C5274,「_Alignas
不再套用至類型 』Inner'(僅適用於宣告的數據物件)」現在會在警告層級 1 發出。
在舊版 Visual Studio 中, _Alignas
在匿名類型宣告旁邊出現時,會忽略它。 例如:
// compile with /std:c17
#include <stddef.h>
struct S {
_Alignas(32) struct { int anon_member; };
int k;
};
static_assert(offsetof(struct S, k)==4, "incorrect offsetof");
static_assert(sizeof(struct S)==32, "incorrect size");
先前,編譯此程序代碼時,這兩 static_assert
個語句都會失敗。 程式代碼現在會編譯,但具有下列層級 1 警告:
warning C5274: behavior change: _Alignas no longer applies to the type '<unnamed-tag>' (only applies to declared data objects)
warning C5273: behavior change: _Alignas on anonymous type no longer ignored (promoted members will align)
如果您要先前的行為,請將 取代 _Alignas(N)
為 __declspec(align(N))
。 不同於 _Alignas
, declspec(align)
可以套用至型別。
__VA_OPT__
啟用為 下方的延伸模組 /Zc:preprocessor
__VA_OPT__
已新增至 C++20 和 C23。 在新增之前,沒有標準方式在 variadic 宏中以逗號滑行。 若要提供更好的回溯相容性, __VA_OPT__
請在所有語言版本的令牌式預處理器 /Zc:preprocessor
下啟用。
例如,現在這會編譯而不會發生錯誤:
#define LOG_WRAPPER(message, ...) WRITE_LOG(__LINE__, message __VA_OPT__(, __VA_ARGS__))
// Failed to build under /std:c11, now succeeds.
LOG_WRAPPER("Log message");
LOG_WRAPPER("Log message with %s", "argument")
C23 語言
針對 C23,使用 /std:clatest
編譯程式參數時可以使用下列專案:
下列適用於所有 C 語言版本:
C++ 標準程式庫
C++23 功能
formattable
、range_format
、format_kind
和set_debug_format()
作為P2286R8格式範圍的一部分<mdspan>
每個 P0009R18 和後續套用至 C++23 Standard 的措辭變更。format()
每個 P2510R3指標。
Visual Studio 2022 17.8 版中的一致性改善
Visual Studio 2022 17.8 版包含 Microsoft C/C++ 編譯程式中的下列一致性改進、錯誤修正和行為變更。
/FU
發出錯誤
C 編譯程式用來接受 /FU
選項,即使它尚未支援 Managed 編譯一段時間也一樣。 它現在會發出錯誤。 傳遞此選項的專案只需要將它限制為 C++/CLI 專案。
C++ 標準程式庫
C++23 具名模組 std
,現在 std.compat
可在使用 /std:c++20
編譯時使用 。
如需對 C++ 標準連結庫所做的變更更廣泛摘要,請參閱 STL Changelog VS 2022 17.8。
Visual Studio 2022 17.7 版的一致性改善
Visual Studio 2022 17.7 版包含下列 Microsoft C/C++ 編譯程式中醒目提示的一致性改善、錯誤修正和行為變更。
已新增 /std:clatest
至 C 編譯程式
這個參數的行為就像 C++ 編譯程式的參數一樣 /std:c++latest
。 參數可啟用針對下一個 C 標準草稿提出的所有目前實作編譯程式和標準連結庫功能,以及一些進行中和實驗性功能。
C++ 標準程式庫
現在支持連結 <print>
庫。 請參閱 P2093R14格式化輸出。
已實作 views::cartesian_product
。
如需對標準範本連結庫所做的變更更廣泛摘要,請參閱 STL Changelog VS 2022 17.7。
using
一致性
先前,指示 using
詞可能會導致使用之命名空間的名稱在不應該顯示時保持可見。 這可能會導致不合格的名稱查閱在命名空間中尋找名稱,即使沒有 using
作用中的指示詞。
以下是新舊行為的一些範例。
下列批註中的參考 「(1)」 表示在命名空間A
中呼叫 f<K>(t)
:
namespace A
{
template<typename K, typename T>
auto f2(T t)
{
return f<K>(t); // (1) Unqualified lookup should not find anything
}
}
namespace B
{
template<typename K, typename T>
auto f(T t) noexcept
{ // Previous behavior: This function was erroneously found during unqualified lookup at (1)
return A::f2<K>(t);
}
}
namespace C
{
template<typename T>
struct S {};
template<typename, typename U>
U&& f(U&&) noexcept; // New behavior: ADL at (1) correctly finds this function
}
namespace D
{
using namespace B;
void h()
{
D::f<void>(C::S<int>());
}
}
相同的基礎問題可能會導致先前編譯的程式代碼現在遭到拒絕:
#include <memory>
namespace Addin {}
namespace Gui
{
using namespace Addin;
}
namespace Addin
{
using namespace std;
}
// This previously compiled, but now emits error C2065 for undeclared name 'allocator'.
// This should be declared as 'std::allocator<T*>' because the using directive nominating
// 'std' is not active at this point.
template <class T, class U = allocator<T*>>
class resource_list
{
};
namespace Gui
{
typedef resource_list<int> intlist;
}
Visual Studio 2022 17.6 版中的一致性改善
Visual Studio 2022 17.6 版包含下列 Microsoft C/C++ 編譯程式中的一致性改進、錯誤修正和行為變更。
複合 volatile
指派不再過時
C++20 已被取代,將特定運算符套用至符合 volatile
的型別。 例如,使用 編譯 cl /std:c++20 /Wall test.cpp
下列程式代碼時:
void f(volatile int& expr)
{
++expr;
}
編譯程式會產生 test.cpp(3): warning C5214: applying '++' to an operand with a volatile qualified type is deprecated in C++20
。
在 C++20 中,複合指派運算子(格式 @=
的運算子)已被取代。 在 C++23 中,C++20 中排除的複合運算元不再過時。 例如,在 C++23 中,下列程式代碼不會產生警告,而在 C++20 中則會產生警告:
void f(volatile int& e1, int e2)
{
e1 += e2;
}
如需此變更的詳細資訊,請參閱 CWG:2654
在運算式中重寫相等比較較不重大變更 (P2468R2)
在 C++20 中, P2468R2 變更編譯程式以接受下列程式代碼:
struct S
{
bool operator==(const S&);
bool operator!=(const S&);
};
bool b = S{} != S{};
編譯程式接受此程式代碼,這表示編譯程式對程式代碼更為嚴格,例如:
struct S
{
operator bool() const;
bool operator==(const S&);
};
bool b = S{} == S{};
編譯程式 17.5 版接受此程式。 17.6 版的編譯程式會拒絕它。 若要修正此問題,請新增 const
至 operator==
以移除模棱兩可。 或者,新增對應 operator!=
至定義,如下列範例所示:
struct S
{
operator bool() const;
bool operator==(const S&);
bool operator!=(const S&);
};
bool b = S{} == S{};
Microsoft C/C++ 編譯程式 17.5 和 17.6 版接受先前的程式,並在這兩個版本中呼叫 S::operator==
。
P2468R2中所述的一般程序設計模型是,如果類型有對應的 operator!=
,通常會隱藏重寫行為。 針對先前在 C++17 中編譯的程式代碼,新增對應的 operator!=
修正建議。 如需詳細資訊,請參閱 程序設計模型。
Visual Studio 2022 17.4 版中的一致性改善
Visual Studio 2022 17.4 版包含 Microsoft C/C++ 編譯程式中的下列一致性改進、錯誤修正和行為變更。
沒有固定類型之未限定範圍 enum
的基礎類型
在 Visual Studio 2022 17.4 版之前的 Visual Studio 版本中,C++ 編譯程式無法正確判斷沒有固定基底類型的未範圍列舉的基礎類型。 在 下 /Zc:enumTypes
,我們現在正確地實作標準行為。
C++ 標準需要 的基礎型 enum
別足以容納該 enum
中的所有列舉值。 足夠大的列舉值可以將 的基礎型 enum
別設定為 unsigned int
、 long long
或 unsigned long long
。 先前,不論列舉值為何,這類 enum
類型在 Microsoft 編譯程式中一律會有 基礎型 int
別。
啟用時, /Zc:enumTypes
此選項是潛在的來源和二進位中斷性變更。 默認為關閉,且未啟用 /permissive-
,因為修正可能會影響二進位相容性。 啟用一致性修正時,某些列舉型別會變更大小。 某些 Windows SDK 標頭包含這類列舉定義。
範例
enum Unsigned
{
A = 0xFFFFFFFF // Value 'A' does not fit in 'int'.
};
// Previously, failed this static_assert. Now passes with /Zc:enumTypes.
static_assert(std::is_same_v<std::underlying_type_t<Unsigned>, unsigned int>);
template <typename T>
void f(T x)
{
}
int main()
{
// Previously called f<int>, now calls f<unsigned int>.
f(+A);
}
// Previously this enum would have an underlying type of `int`, but Standard C++ requires this to have
// a 64-bit underlying type. Using /Zc:enumTypes changes the size of this enum from 4 to 8, which could
// impact binary compatibility with code compiled with an earlier compiler version or without the switch.
enum Changed
{
X = -1,
Y = 0xFFFFFFFF
};
定義中沒有固定基礎型別的 enum
列舉值類型
在 Visual Studio 2022 17.4 版之前的 Visual Studio 版本中,C++ 編譯程式未正確建立列舉值類型的模型。 它可能會假設列舉中沒有固定基礎類型的不正確類型,然後才有列舉的右大括號。 在 下 /Zc:enumTypes
,編譯程式現在正確地實作標準行為。
C++ 標準會指定在不含固定基礎型別的列舉定義中,初始化表達式會決定列舉值的類型。 或者,對於沒有初始化表達式的列舉值,請依前一個列舉值的類型(考慮溢位)。 先前,這類列舉值一律會獲得列舉的推算類型,並具有基礎型別的佔位元(通常是 int
)。
啟用時, /Zc:enumTypes
此選項是潛在的來源和二進位中斷性變更。 默認為關閉,且未啟用 /permissive-
,因為修正可能會影響二進位相容性。 啟用一致性修正時,某些列舉型別會變更大小。 某些 Windows SDK 標頭包含這類列舉定義。
範例
enum Enum {
A = 'A',
B = sizeof(A)
};
static_assert(B == 1); // previously failed, now succeeds under /Zc:enumTypes
在此範例中,列舉值 A
應該在列舉的右大括弧之前具有 類型 char
,因此 B
應該使用 sizeof(char)
初始化。 在/Zc:enumTypes
修正之前,A
具有具有推斷基礎型別 的列舉型Enum
int
別,並使用 B
或 4 初始化sizeof(Enum)
。
Visual Studio 2022 17.3 版的一致性改善
Visual Studio 2022 17.3 版包含 Microsoft C/C++ 編譯程式中的下列一致性改進、錯誤修正和行為變更。
C:改善指標之間的修飾詞相容性檢查
C 編譯程式未正確比較指標之間的修飾詞,特別是 void*
。 此瑕疵可能會導致與之間的const int**
不相容與 void*
相容性的int* volatile*
void*
不正確診斷。
範例
void fn(void* pv) { (pv); }
int main()
{
int t = 42;
int* pt = &t;
int* volatile * i = &pt;
fn(i); // Now raises C4090
const int** j = &pt;
fn(j); // No longer raises C4090
}
Visual Studio 2022 17.2 版中的一致性改善
Visual Studio 2022 17.2 版包含下列 Microsoft C/C++ 編譯程式中的一致性改進、錯誤修正和行為變更。
未決定的雙向字元警告
Visual Studio 2022 17.2 版會針對批注和字串中未確定的 Unicode 雙向字元新增層級 3 警告 C5255。 警告解決了特洛伊木馬來源中所述 的安全性問題:尼古拉斯·鮑徹和羅斯·安德森的看不見弱點 。 如需 Unicode 雙向字元的詳細資訊,請參閱 Unicode® 標準附錄 #9:UNICODE BIDIRECTIONAL ALGORITHM。
警告 C5255 只會處理轉換后包含 Unicode 雙向字元的檔案。 此警告適用於UTF-8、UTF-16和UTF-32檔案,因此必須提供適當的來源編碼。 這項變更是來源中斷性變更。
範例(前後)
在 Visual Studio 2022 17.2 版之前的 Visual Studio 版本中,未確定的雙向字元不會產生警告。 Visual Studio 2022 17.2 版會產生警告 C5255:
// bidi.cpp
int main() {
const char *access_level = "user";
// The following source line contains bidirectional Unicode characters equivalent to:
// if ( strcmp(access_level, "user\u202e \u2066// Check if admin \u2069 \u2066") ) {
// In most editors, it's rendered as:
// if ( strcmp(access_level, "user") ) { // Check if admin
if ( strcmp(access_level, "user // Check if admin ") ) {
printf("You are an admin.\n");
}
return 0;
}
/* build output
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+202e'
bidi.cpp(8): warning C5255: unterminated bidirectional character encountered: 'U+2066'
*/
from_chars()
float
tiebreaker
Visual Studio 2022 17.2 版修正了 tiebreaker 規則中產生不正確結果的錯誤 <charconv>
from_chars()
float
。 這個錯誤會影響在窄範圍內,位於連續 float
值確切中間點的小數點字串。 (受影響值最小和最大分別為 32768.009765625
和 131071.98828125
。平局規則想要四捨五入為「偶數」,而「甚至」碰巧是“向下”,但實作錯誤地捨入“向上”(double
不受影響)。如需詳細資訊和實作詳細數據,請參閱 microsoft/STL#2366。
這項變更會影響指定案例範圍內的運行時間行為:
範例
// from_chars_float.cpp
#include <cassert>
#include <charconv>
#include <cstdio>
#include <string_view>
#include <system_error>
using namespace std;
int main() {
const double dbl = 32768.009765625;
const auto sv = "32768.009765625"sv;
float flt = 0.0f;
const auto result = from_chars(sv.data(), sv.data() + sv.size(), flt);
assert(result.ec == errc{});
printf("from_chars() returned: %.1000g\n", flt);
printf("This rounded %s.\n", flt < dbl ? "DOWN" : "UP");
}
在 Visual Studio 2022 17.2 版之前的版本:
C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.01171875
This rounded UP.
在 Visual Studio 2022 17.2 版和之後:
C:\Temp>cl /EHsc /nologo /W4 /std:c++17 from_chars_float.cpp && from_chars_float
from_chars_float.cpp
from_chars() returned: 32768.0078125
This rounded DOWN.
/Zc:__STDC__
讓 __STDC__
C 可供使用
C 標準要求符合 C 實作定義為 __STDC__
1
。 由於 UCRT 的行為不會在 為 1
時__STDC__
公開 POSIX 函式,因此預設無法為 C 定義此宏,而不需要對穩定語言版本進行重大變更。 Visual Studio 2022 17.2 版和更新版本會新增定義此宏的一致性選項 /Zc:__STDC__
。 沒有選項的負版本。 目前,我們計劃針對未來的 C 版本預設使用此選項。
這項變更是來源中斷性變更。 當啟用 C11 或 C17 模式時, /std:c11
/std:c17
或 和 一起 /Zc:__STDC__
適用。
範例
// test__STDC__.c
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
#if __STDC__
int f = _open("file.txt", _O_RDONLY);
_close(f);
#else
int f = open("file.txt", O_RDONLY);
close(f);
#endif
}
/* Command line behavior
C:\Temp>cl /EHsc /W4 /Zc:__STDC__ test__STDC__.c && test__STDC__
*/
遺漏大括弧的警告
警告 C5246 會在子對象的匯總初始化期間報告遺漏大括弧。 在 Visual Studio 2022 17.2 版之前,警告未處理匿名 struct
或 union
的情況。
這項變更是來源中斷性變更。 當啟用 off-by-by-default 警告 C5246 時,就會套用它。
範例
在 Visual Studio 2022 17.2 版和更新版本中,此程式代碼現在會導致錯誤:
struct S {
union {
float f[4];
double d[2];
};
};
void f()
{
S s = { 1.0f, 2.0f, 3.14f, 4.0f };
}
/* Command line behavior
cl /Wall /c t.cpp
t.cpp(10): warning C5246: 'anonymous struct or union': the initialization of a subobject should be wrapped in braces
*/
若要解決此問題,請將大括弧新增至初始化表達式:
void f()
{
S s = { { 1.0f, 2.0f, 3.14f, 4.0f } };
}
Visual Studio 2022 17.1 版中的一致性改善
Visual Studio 2022 17.1 版包含下列 Microsoft C/C++ 編譯程式中的一致性改進、錯誤修正和行為變更。
在非本機 Lambda 表達式中偵測格式不正確的擷取預設值
C++ Standard 只允許區塊範圍中的 Lambda 表達式具有擷取預設值。 在 Visual Studio 2022 17.1 版和更新版本中,編譯程式會在非本機 Lambda 表達式中不允許擷取預設值時偵測。 它會發出新的層級 4 警告 C5253。
這項變更是來源中斷性變更。 它適用於任何使用新 Lambda 處理器的模式: /Zc:lambda
、 /std:c++20
或 /std:c++latest
。
範例
在 Visual Studio 2022 17.1 版中,此程式代碼現在發出錯誤:
#pragma warning(error:5253)
auto incr = [=](int value) { return value + 1; };
// capture_default.cpp(3,14): error C5253: a nonlocal lambda cannot have a capture default
// auto incr = [=](int value) { return value + 1; };
// ^
若要修正此問題,請移除擷取預設值:
#pragma warning(error:5253)
auto incr = [](int value) { return value + 1; };
C4028 現在是適用於函式對指標作業的 C4133
在 Visual Studio 2022 17.1 版之前,編譯程式會在 C 程式代碼中的特定指針對函式比較上回報錯誤訊息。 當您比較兩個具有相同自變數計數但不相容類型的函式指標時,報告了不正確的訊息。 現在,我們會發出不同的警告,抱怨指針對函式不相容,而不是函式參數不符。
這項變更是來源中斷性變更。 當程式代碼編譯為 C 時,就會套用它。
範例
int f1(int);
int f2(char*);
int main(void)
{
return (f1 == f2);
}
// Old warning:
// C4028: formal parameter 1 different from declaration
// New warning:
// C4113: 'int (__cdecl *)(char *)' differs in parameter lists from 'int (__cdecl *)(int)'
非ependent 上的錯誤 static_assert
在 Visual Studio 2022 17.1 版和更新版本中,如果與 static_assert
關聯的表達式不是相依表達式,編譯程式會在剖析表達式時評估表達式。 如果表達式評估為 false
,編譯程式就會發出錯誤。 先前,如果 static_assert
是在函式範本的主體內(或類別範本的成員函式主體內),則編譯程式不會執行此分析。
這項變更是來源中斷性變更。 它會套用在任何表示 /permissive-
或 /Zc:static_assert
的模式中。 您可以使用 /Zc:static_assert-
編譯程式選項來停用此行為變更。
範例
在 Visual Studio 2022 17.1 版和更新版本中,此程式代碼現在會造成錯誤:
template<typename T>
void f()
{
static_assert(false, "BOOM!");
}
若要修正此問題,請讓表達式相依。 例如:
template<typename>
constexpr bool dependent_false = false;
template<typename T>
void f()
{
static_assert(dependent_false<T>, "BOOM!");
}
使用此變更時,編譯程式只會在具現化函式範本 f
時發出錯誤。
Visual Studio 2022 17.0 版的一致性改善
Visual Studio 2022 17.0 版包含 Microsoft C/C++ 編譯程式中的下列一致性改進、錯誤修正和行為變更。
列舉類型的位欄位寬度警告
當您將列舉類型的實例宣告為位欄位時,位欄位的寬度必須容納列舉的所有可能值。 否則,編譯程式會發出診斷訊息。 請考慮此範例:請考慮:
enum class E : unsigned { Zero, One, Two };
struct S {
E e : 1;
};
程序設計人員可能會預期類別成員 S::e
可以保存任何明確命名 enum
的值。 假設列舉項目的數目,則不可能。 bitfield 無法涵蓋明確提供值E
的範圍(在概念上為的E
定義域)。 為了解決位欄位寬度不足以容納列舉域的問題,會將新的 (off) 警告新增至 MSVC:
t.cpp(4,5): warning C5249: 'S::e' of type 'E' has named enumerators with values that cannot be represented in the given bit field width of '1'.
E e : 1;
^
t.cpp(1,38): note: see enumerator 'E::Two' with value '2'
enum class E : unsigned { Zero, One, Two };
^
此編譯程式行為是會影響所有 /std
和 /permissive
模式的來源和二進位中斷性變更。
排序指標比較或 nullptr
0 時發生錯誤
C++ 標準無意中允許或 0 的 nullptr
已排序指標比較。 例如:
bool f(int *p)
{
return p >= 0;
}
WG21 檔 N3478 移除了這項監督。 這項變更會在 MSVC 中實作。 使用 和 /diagnostics:caret
編譯範例/permissive-
時,它會發出下列錯誤:
t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
return p >= 0;
^
此編譯程式行為是一種來源和二進位中斷性變更,會影響在所有/std
模式中使用 /permissive-
編譯的程序代碼。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應