Visual Studio 2022 中的 C++ 一致性改善、行為變更和錯誤 (bug) 修正
Visual Studio 中的 Microsoft C/C++ (MSVC) 在每個版本中都進行一致性改善和錯誤 (bug) 修正。 本文依主要版次和版本的順序列出重大改善。 若要直接跳至特定版本的變更,請使用 本文 頂端的 [在本文中] 連結。
本文件列出 Visual Studio 2022 中的變更。
如需舊版 Visual Studio 中的變更:
版本 | 一致性改善連結 |
---|---|
2019 | Visual Studio 2019 中的 C++ 一致性改善 |
2017 | Visual Studio 2017 中的 C++ 一致性改善 |
2003-2015 | 從 2003 到 2015 的 Visual C++ 新功能 |
Visual Studio 2022 17.12 版中的一致性改善
Visual Studio 2022 17.12 版包含下列一致性改善、錯誤修正,以及Microsoft C/C++ 編譯程序的行為變更。
如需對標準範本庫所做的變更的深入摘要,包括一致性變更、錯誤修正和效能改善,請參閱 STL Changelog VS 2022 17.12。
_com_ptr_t::operator bool()
現在明確
這是來源/二進位中斷性變更。
從實例的隱含轉換bool
_com_ptr_t
可能會出人意料或導致編譯程序錯誤。 C++ Core Guidelines (C.164)不建議隱含轉換函式,並_com_ptr_t
包含對 和Interface*
的bool
隱含轉換。 這兩個隱含轉換可能會導致模棱兩可。
為了協助解決此問題,現在的轉換 bool
是明確的。 的轉換 Interface*
不會變更。
提供巨集以退出這個新行為,並還原先前的隱含轉換。 使用 /D_COM_DISABLE_EXPLICIT_OPERATOR_BOOL
編譯以退出這項變更。 建議您修改程序代碼,不要依賴隱含轉換。
例如:
#include <comip.h>
template<class Iface>
using _com_ptr = _com_ptr_t<_com_IIID<Iface, &__uuidof(Iface)>>;
int main()
{
_com_ptr<IUnknown> unk;
if (unk) // Still valid
{
// ...
}
bool b = unk; // Still valid.
int v = unk; // Previously permitted, now emits C2240: cannot convert from '_com_ptr_t<_com_IIID<IUnknown,& _GUID_00000000_0000_0000_c000_000000000046>>' to 'int'
}
常數表達式不再一律 noexcept
處於寬鬆模式
這是來源/二進位中斷性變更。
常數表達式一律 noexcept
為 ,即使它涉及以可能擲回例外狀況規格宣告之函式的函式呼叫也一樣。 雖然 Microsoft Visual C++ 編譯程式在所有C++語言版本中仍支援此措辭,但在 C++17 中已移除此 /permissive
措辭。
拿掉此 /permissive
模式行為。 常數表達式不再提供特殊的隱含行為。
函 noexcept
式上的 constexpr
規範現在在所有模式中都會受到尊重。 需要這項變更,才能正確實作依賴標準 noexcept
行為之後續核心問題解決方式。
例如:
constexpr int f(bool b) noexcept(false)
{
if (b)
{
throw 1;
}
else
{
return 1;
}
}
void g(bool b)
{
noexcept(f(b)); // false. No change to behavior
noexcept(f(true)); // false. No change to behavior
noexcept(f(false)); // false. Was true in /permissive mode only in previous versions.
}
Visual Studio 2022 17.11 版中的一致性改善
Visual Studio 2022 17.11 版包含下列一致性改善、錯誤修正,以及Microsoft C/C++ 編譯程序的行為變更。
如需對標準範本連結庫所做的變更的深入摘要,包括一致性變更、錯誤修正和效能改善,請參閱 STL Changelog VS 2022 17.11。
使用列印空白行 println
根據 P3142R0,現在很容易使用 println
產生空白行。 使用 編譯 /std:c++latest
時,可以使用這項功能。
在這項變更之前,您已撰寫: println("");
現在您撰寫: println();
。
println();
相當於println(stdout);
println(FILE* stream);
相當於println(stream, "\n");
實現 range_formatter
現在會range_formatter
實作每個P2286R8。 使用 編譯 /std:c++latest
時,可以使用這項功能。
Visual Studio 2022 17.10 版中的一致性改善
Visual Studio 2022 17.10 版包含下列一致性改善、錯誤修正,以及 C/C++ 編譯程式中Microsoft行為變更。
有關標準範本庫所做變更的深入摘要,包括符合性變更、錯誤修正和效能改進,請參閱 STL 變更記錄 VS 2022 17.10。
使用明確指定的傳回型別進行轉換運算子特製化
在某些情況下,編譯器過去常常不正確地將轉換運算子特製化,這可能會導致傳回型別不相符。 這些無效的特製化不再發生。 這是原始程式碼中斷性變更。
// Example 1
struct S
{
template<typename T> operator const T*();
};
void test()
{
S{}.operator int*(); // this is invalid now
S{}.operator const int*(); // this is valid
}
// Example 2
// In some cases, the overload resolution result may change
struct S
{
template <typename T> operator T*(); // overload 1
template <typename T> operator const T*(); // overload 2
};
void test()
{
S{}.operator int*(); // this used to call overload 2, now it calls overload 1
}
已新增對 #elifdef
和 #elifndef
的支援
已新增對 WG21 P2334R1 (C++23) 和 WG14 N2645 (C++23) 的支援,引進 #elifdef
和 #elifndef
前置處理器指示詞。
需要 /std:clatest
或 /std:c++latest
。
之前:
#ifdef __cplusplus
#include <atomic>
#elif !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
#else
#include <custom_atomics_library.h>
#endif
:
#ifdef __cplusplus
#include <atomic>
#elifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#else
#include <custom_atomics_library.h>
#endif
在 C 中的結構化型別上套用 _Alignas
適用於 C 語言 (C17 和更新版本)。 也新增至 Microsoft Visual Studio 17.9
在 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 個位元組的位移。
這是二進位中斷性變更,因此當這項變更生效時,現在會發出警告。 警告 C5274 現在會針對先前範例在警告層級 1 發出: warning C5274: behavior change: _Alignas no longer applies to the type 'Inner' (only applies to declared data objects)
。
此外,在舊版的 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)
會套用至型別。
改善警告 C4706
這是原始程式碼中斷性變更。 先前編譯器未偵測到在括號中包裝指派的慣例,如果指派是有意為之,則會隱藏有關條件運算式內指派的 警告 C4706。 編譯器現在會偵測括號並隱藏警告。
#pragma warning(error: 4706)
struct S
{
auto mf()
{
if (value = 9)
return value + 4;
else
return value;
}
int value = 9;
};
編譯器現在也會在未參考函式時發出警告。 先前因為 mf
是未參考的內嵌函式,因此不會為此程式碼發出警告 C4706。 現在會發出警告:
error C4706: assignment used as a condition
note: if an assignment is intended you can enclose it in parentheses, '(e1 = e2)', to silence this warning
若要修正此警告,請使用等號運算子 value == 9
,如果這是預期行為。 或者,如果指派是有意為之,請將指派包裝在括號中,(value = 9)
。 否則,由於函式未參考,請將其移除。
Visual Studio 2022 17.9 版中的一致性改善
Visual Studio 2022 17.9 版包含下列一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
如需對標準範本庫所做變更的更廣泛摘要,請參閱 STL 變更記錄 VS 2022 17.9。
在 C 中的結構化型別上套用 _Alignas
在 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 個位元組位移。
修正這個問題是二進位中斷性變更,因此當套用此行為變更時,就會發出警告。 針對上述程式碼,現在會在警告層級 1 發出警告 C5274「_Alignas
不再適用於型別 'Inner' (僅適用於已宣告資料物件)」。
在舊版 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。 在新增之前,沒有在可變參數巨集中省略逗號的標準方式。 為了提供更好的回溯相容性,__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 格式化範圍的一部分- 每個 P0009R18 的
<mdspan>
和套用至 C++23 標準的後續字組變更。 - 每個 P2510R3 的
format()
指標。
Visual Studio 2022 17.8 版中的一致性改善
Visual Studio 2022 17.8 版包含下列一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
/FU
發出錯誤
C 編譯器用來接受 /FU
選項,即使一段時間不支援受控編譯也一樣。 現在會發出錯誤。 傳遞此選項的專案需要將其限制為僅限 C++/CLI 專案。
C++ 標準程式庫
使用 /std:c++20
編譯時,現在可以使用 C++23 具名模組 std
和 std.compat
。
如需對 C++ 標準程式庫所做變更的更廣泛摘要,請參閱 STL 變更記錄 VS 2022 17.8。
Visual Studio 2022 17.7 版中的一致性改善
Visual Studio 2022 17.7 版包含下列醒目提示一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
已新增 /std:clatest
至 C 編譯器
此參數的行為就像是 C++ 編譯器 /std:c++latest
參數的行為。 該參數可啟用針對下一個 C 標準草稿提出的所有目前實作編譯器和標準程式庫功能,以及一些進行中和實驗性的功能。
C++ 標準程式庫
現在支援 <print>
程式庫。 請參閱 P2093R14 格式化輸出。
已實作 views::cartesian_product
。
如需對標準範本庫所做變更的更廣泛摘要,請參閱 STL 變更記錄 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 版包含下列一致性改善、錯誤 (bug) 修正和 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 版包含下列一致性改善、錯誤 (bug) 修正和 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
,因此應該使用 sizeof(char)
初始化 B
。 在 /Zc:enumTypes
修正之前,A
具有列舉型別 Enum
,其具有推算基礎型別 int
,且 B
是使用 sizeof(Enum)
來初始化,或 4。
Visual Studio 2022 17.3 版中的一致性改善
Visual Studio 2022 17.3 版包含下列一致性改善、錯誤 (bug) 修正和 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 版包含下列一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
未結束的雙向字元警告
Visual Studio 2022 17.2 版會針對註解和字串中未結束的 Unicode 雙向字元新增層級 3 警告 C5255。 警告解決了 Nicholas Boucher 和 Ross Anderson 所撰寫的 Trojan Source: Invisible Vulnerabilities (特洛伊木馬來源:看不見的弱點) 中所述的安全性顧慮。 如需 Unicode 雙向字元的詳細資訊,請參閱 Unicode® Standard Annex #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 版修正了 <charconv>
from_chars()
float
tiebreaker 規則中產生不正確結果的錯誤 (bug)。 這個錯誤 (bug) 會影響在狹窄範圍內連續 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 的行為,當 __STDC__
為 1
時不會公開 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
的情況。
此變更是來源中斷性變更。 當預設關閉警告 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 版包含下列一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
在非本機 Lambda 運算式中偵測到格式不正確的擷取預設值
C++ 標準只允許區塊範圍中的 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)'
非相依 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 版包含下列一致性改善、錯誤 (bug) 修正和 Microsoft C/C++ 編譯器的行為變更。
列舉型別的位元欄位寬度警告
當您將列舉型別的執行個體宣告為位元欄位時,位元欄位的寬度必須能夠容納列舉的所有可能值。 否則,編譯器會發出診斷訊息。 請考慮此範例:考慮:
enum class E : unsigned { Zero, One, Two };
struct S {
E e : 1;
};
程式設計人員可能會預期類別成員 S::e
可以保留任何明確命名 enum
值。 若有列舉元素的數目,則不可能。 位元欄位無法涵蓋明確提供 E
值的範圍 (就概念而言,E
的網域)。 為了解決位元欄位寬度不足以容納列舉網域的顧慮,會將新的 (預設關閉) 警告新增至 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++ 標準不小心允許針對 nullptr
或 0 的排序指標比較。 例如:
bool f(int *p)
{
return p >= 0;
}
WG21 文件 N3478 移除了這項監督。 這項變更會在 MSVC 中實作。 使用 /permissive-
(和 /diagnostics:caret
) 編譯範例時,會發出下列錯誤:
t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
return p >= 0;
^
此編譯器行為是會影響在所有 /std
模式中使用 /permissive-
進行編譯之程式碼的來源和二進位中斷性變更。