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版之前,發出診斷「不正確的對齊方式」。 編譯程式在 member2struct 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))。 不同於 _Alignasdeclspec(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 編譯程式參數時可以使用下列專案:

typeof
typeof_unqual

下列適用於所有 C 語言版本:

__typeof__
__typeof_unqual__

C++ 標準程式庫

C++23 功能

  • formattablerange_formatformat_kindset_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 版的編譯程式會拒絕它。 若要修正此問題,請新增 constoperator== 以移除模棱兩可。 或者,新增對應 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 intlong longunsigned 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具有具有推斷基礎型別 的列舉型Enumint別,並使用 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.009765625131071.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 版之前,警告未處理匿名 structunion的情況。

這項變更是來源中斷性變更。 當啟用 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- 編譯的程序代碼。

另請參閱

Microsoft C/C++ 語言一致性