Visual Studio 2022 版本 17.4 中的一致性改进
Visual Studio 2022 版本 17.4 包含 Microsoft C/C++ 编译器中的以下一致性改进、错误修复和行为更改
作用域的基础类型没有固定类型enum
在 Visual Studio 2022 版本 17.4 之前的 Visual Studio 版本中,C++编译器未正确确定没有固定基类型的无作用域枚举的基础类型
C++ 标准要求枚举的基础类型足够大,以容纳该枚举中的所有枚举器。足够大的枚举器可以将枚举的基础类型设置为无符号 int、long long 或无符号 long long。以前,无论枚举器值如何,此类枚举类型在 Microsoft 编译器中始终具有基础类型int
启用后,/Zc:enumType选项是潜在的源和二进制中断性更改。默认情况下,它处于关闭状态,并且不由/允许启用,因为修复可能会影响二进制兼容性。启用一致性修复时,某些枚举类型会更改大小。某些 Windows SDK 标头包含此类枚举定义。
例
#include<stdio.h> enum Unsigned { A = 0xFFFFFFFF //值“A”不适合“int”。 }; //以前,未通过此static_assert。现在使用 /Zc:enumTypes 传递。 static_assert(std::is_same_v<std::underlying_type_t<Unsigned>, unsigned int>); template <typename T> void f(T x) { } int main() { // 以前称为 f<int>,现在称为 f<unsigned int>。 f(+A); } //以前,这个枚举将具有底层类型的“int”,但标准C++要求它具有 //64 位基础类型。使用 / Zc:enumTypes 将此枚举的大小从 4 更改为 8,这可能会 //影响与使用早期编译器版本或不使用开关编译的代码的二进制兼容性。 enum Changed { X = -1, Y = 0xFFFFFFFF };
定义中没有固定基础类型的枚举器类型enum
在 Visual Studio 2022 版本 17.4 之前的 Visual Studio 版本中,C++编译器未正确确定没有固定基类型的无作用域枚举的基础类型
C++ 标准要求枚举的基础类型足够大,以容纳该枚举中的所有枚举器。足够大的枚举器可以将枚举的基础类型设置为无符号 int、long long 或无符号 long long。以前,无论枚举器值如何,此类枚举类型在 Microsoft 编译器中始终具有基础类型int。
启用后,/Zc:enumType选项是潜在的源和二进制中断性更改。默认情况下,它处于关闭状态,并且不由/允许启用,因为修复可能会影响二进制兼容性。启用一致性修复时,某些枚举类型会更改大小。某些 Windows SDK 标头包含此类枚举定义。
举例
enum Enum { A = 'A', B = sizeof(A) }; static_assert(B == 1); // 以前失败,现在在 /Zc:enumType 下成功
枚举器应在枚举的右大括号之前具有char类型,因此应使用 进行初始化。在 /Zc:enumType修复之前,具有枚举类型,具有推导的底层类型int,并使用 或 4 进行初始化。ABsizeof(char)AEnumBsizeof(Enum)
Visual Studio 2022 版本 17.3 中的一致性改进
Visual Studio 2022 版本 17.3 包含 Microsoft C/C++ 编译器中的以下一致性改进、错误修复和行为更改。
改进了指针之间的修饰符兼容性检查
尤其是 C 编译器没有正确比较指针之间的修饰符。此缺陷可能导致对两者之间的不相容性和两者之间的不相容性诊断不正确
void fn(void* pv) { (pv); } int main() { int t = 42; int* pt = &t; int* volatile * i = &pt; fn(i); // 现在支持 C4090 const int** j = &pt; fn(j); //不再支持C4090 }
Visual Studio 2022 版本 17.2 中的一致性改进
Visual Studio 2022 版本 17.2 包含 Microsoft C/C++ 编译器中的以下一致性改进、错误修复和行为更改。
未终止的双向字符警告
- Visual Studio 2022 版本 17.2 为注释和字符串中未终止的 Unicode 双向字符添加了 3 级警告 C5255
- 警告 C5255 仅处理转换后包含 Unicode 双向字符的文件。此警告适用于 UTF-8、UTF-16 和 UTF-32 文件,因此必须提供正确的源编码。此更改是源中断性更改。
示例(之前/之后)
代码举例
// bidi.cpp int main() { const char *access_level = "user"; //以下源代码行包含等效于的双向 Unicode 字符: if ( strcmp(access_level, “user // Check if admin ”) ) { 在大多数编辑器中,它呈现为: if ( strcmp(access_level, "user") ) { // Check if admin if ( strcmp(access_level, "user // Check if admin ") ) { printf("You are an admin.\n"); } return 0; } /* 构建输出 双向.cpp(8): 警告 C5255:遇到未终止的双向字符:“U+202e” 双向.cpp(8): 警告 C5255:遇到未终止的双向字符:“U+2066” */
from_chars() float
Visual Studio 2022 版本 17.2 修复了产生错误结果的错误破坏规则。此错误影响了位于连续值的精确中点的十进制字符串,范围很窄。(受影响的最小值和最大值分别为 and。决胜规则想要舍入为“偶数”,“偶数”恰好是“向下”,但实现错误地舍入了“向上”
例
// #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.
__STDC__使可用于 C__STDC__
C 标准要求符合 C 的实现定义为 。由于 UCRT 的行为,它不会公开 POSIX 函数,因此默认情况下,如果不对稳定语言版本进行重大更改,则无法为 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 } /* 命令行行为 C:\Temp>cl /EHsc /W4 /Zc:__STDC__ test__STDC__.c && test__STDC__ */
缺少括号的警告
- 警告 C5246 报告在子对象的聚合初始化期间缺少大括号。在 Visual Studio 2022 版本 17.2 之前,警告无法处理匿名者的情况。structunion
- 此更改是源中断性更改。当启用默认关闭警告 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: '匿名结构或联合':子对象的初始化应括在大括号中‘ */
- 若要解决此问题,请向初始值设定项添加大括号
void f() { S s = { { 1.0f, 2.0f, 3.14f, 4.0f } }; }
Visual Studio版本 17.1 中的一致性改进
Visual Studio 2022 版本 17.1 包含 Microsoft C/C++ 编译器中的以下一致性改进、错误修复和行为更改。
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):错误 C5253:非本地 lambda 不能具有捕获默认值 自动增量 = [=](整数值) { 返回值 + 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 及更高版本中,如果与 a 关联的表达式不是依赖表达式,则编译器会在分析表达式后立即计算表达式。如果表达式的计算结果为 ,编译器将发出错误。以前,如果在函数模板的主体中(或在类模板的成员函数的主体中),编译器不会执行此分析。static_assertfalsestatic_assert
此更改是源中断性更改。它适用于任何暗示/允许或/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!"); }
通过此更改,编译器仅在实例化函数模板时发出错误。
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; };
你可能希望类成员可以保存任何显式命名的值。给定枚举元素的数量,这是不可能的。位域不能涵盖显式提供的值的范围(概念上是域)。为了解决位域宽度不足以容纳枚举域的问题,向 MSVC 添加了一条新的(默认情况下为关闭)警告:S::eenumEE
t.cpp(4,5): 警告 C5249: 类型为“E”的“S::e”命名了枚举器,其值无法在给定位字段宽度“1”中表示。
E e : 1;
^
t.cpp(1,38):注:参见值为“2”的枚举器“E::Two”
枚举类 E : 无符号 { 零, 一, 二 };
^
此编译器行为是影响所有/std和/允许模式的源和二进制中断性更改。
与 0 的有序指针比较时出错nullptr
C++标准无意中允许对 0 进行有序指针比较。例如:nullptr
bool f(int *p) { return p >= 0; }
使用 /permissive-(and/diagnostics:caret) 编译示例时,它会发出以下错误:
t.cpp(3,14):错误 C7664:“>=”:指针和整数零(“int *”和“int”)的有序比较
返回 p >= 0;
^
此编译器行为是源和二进制中断性更改,会影响使用/允许全部/std模式编译的代码。
完结撒花!!!