Visual Studio 2022 中的 C++ 一致性改进、行为更改和 bug 修复
Visual Studio 中的 Microsoft C++ (MSVC) 在每个版本中进行了符合性改进和 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.11 中的符合性改进
Visual Studio 2022 版本 17.11 具有以下符合性改进、bug 修复和Microsoft C/C++ 编译器中的行为更改。
有关对标准模板库所做的更改的深入摘要,包括符合性更改、bug 修复和性能改进,请参阅 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 包含 Microsoft C/C++ 编译器中的以下符合性改进、bug 修复和行为变更。
有关对标准模板库所做的更改的深入摘要,包括符合性更改、bug 修复和性能改进,请参阅 STL Changelog 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 包含 Microsoft C/C++ 编译器中的以下符合性改进、bug 修复和行为变更。
有关对标准模板库所做的更改的更广泛摘要,请参阅 STL Changelog 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 字节偏移量对齐。
修复此问题是二进制中断性变更,因此当应用此行为更改时,将发出警告。 对于前面的代码,警告 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
下的扩展启用
C++20 和 C23 中添加了 __VA_OPT__
。 在添加之前,没有一种标准方法可以在可变参数宏中删除逗号。 为了提供更好的后向兼容性,我们在所有语言版本的基于令牌的预处理器 /Zc:preprocessor
下启用了 __VA_OPT__
。
例如,现在编译没有错误:
#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++ 编译器中的以下符合性改进、bug 修复和行为变更。
/FU
发出错误
C 编译器用于接受 /FU
选项,尽管该编译器已经有一段时间不支持托管编译了。 它现在会发出错误。 传递此选项的项目需要将其仅限于 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++ 编译器中的以下符合性改进、bug 修复和行为变更。
向 C 编译器添加了 /std:clatest
此开关的行为类似于 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++ 编译器的以下合规性改进、bug 修复和行为变更。
不再弃用复合 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++ 编译器的以下符合性改进、bug 修复和行为变更。
无固定类型且未区分范围的 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
使用 sizeof(Enum)
进行了初始化,或者是 4。
Visual Studio 2022 版本 17.3 中的符合性改进
Visual Studio 2022 版本 17.3 包含 Microsoft C/C++ 编译器的以下符合性改进、bug 修复和行为变更。
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++ 编译器的以下符合性改进、bug 修复和行为变更。
未终止的双向字符警告
Visual Studio 2022 版本 17.2 为注释和字符串中未终止的 Unicode 双向字符添加了级别 3 警告 C5255。 该警告解决了特洛伊木马来源:不可见的漏洞(作者:Nicholas Boucher 和 Ross Anderson)中所述的安全问题。 有关 Unicode 双向字符的详细信息,请参阅 Unicode® 标准附录 9:UNICODE 双向算法。
警告 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
。)tiebreaker 规则希望舍入到“偶数”,“偶数”刚好是“向下”舍入,但实现却错误地“向上”舍入。(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 包含 Microsoft C/C++ 编译器的以下符合性改进、bug 修复和行为变更。
在非局部 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 包含 Microsoft C/C++ 编译器的以下符合性改进、bug 修复和行为变更。
关于枚举类型位域宽度的警告
将枚举类型的实例声明为位域时,位域的宽度必须容纳枚举的所有可能值。 否则,编译器将发出诊断消息。 请考虑以下示例:
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-
编译的代码。