Улучшения соответствия C++, изменения в поведении и исправления ошибок в Visual Studio 2022
В каждой версии в Microsoft C/C++ в Visual Studio (MSVC) внедряются улучшения в области соответствия стандартам и исправления ошибок. В этой статье перечислены значительные улучшения по основным выпускам, а затем по версиям. Чтобы перейти непосредственно к изменениям для конкретной версии, используйте ссылки в этой статье в верхней части этой статьи.
В этом документе перечислены изменения в Visual Studio 2022.
Для изменений в более ранних версиях Visual Studio:
Версия | Ссылка на улучшения соответствия |
---|---|
2019 | Улучшения соответствия C++ в Visual Studio 2019 |
2017 | Улучшения соответствия C++ в Visual Studio 2017 |
2003-2015 | Новые возможности Visual C++ 2003–2015 |
Улучшения соответствия в Visual Studio 2022 версии 17.13
Visual Studio 2022 версии 17.13 включает следующие улучшения соответствия, исправления ошибок и изменения поведения в компиляторе Microsoft C/C++.
Подробные сведения об изменениях, внесенных в стандартную библиотеку шаблонов, включая изменения соответствия, исправления ошибок и улучшения производительности, см. в статье STL Changelog VS 2022 17.13.
Изменение зависимого от аргумента поиска (ADL)
Языковые конструкции, такие как range-for и структурированные привязки, имеют специальные правила поиска, зависящие от аргументов, для определённых идентификаторов, таких как begin
, end
или get
. Ранее этот поиск включал кандидатов из пространства имен std
, даже если пространство имен std
не является частью обычного набора связанных пространств имен для поиска, зависящего от аргументов.
Программы, которые ввели объявления в std
для этих конструкций, больше не компилируются. Вместо этого объявления должны находиться в обычном связанном пространстве имен для используемых типов (возможно, включая глобальное пространство имен).
template <typename T>
struct Foo {};
namespace std
{
// To correct the program, move these declarations from std to the global namespace
template <typename T>
T* begin(Foo<T>& f);
template <typename T>
T* end(Foo<T>& f);
}
void f(Foo<int> foo)
{
for (auto x : foo) // Previously compiled. Now emits error C3312: no callable 'begin' function found for type 'Foo<int>'
{
...
}
}
Не удается изменить зарезервированные реализацией макросы
Ранее компилятор разрешал изменять или отменять определённость некоторых макросов, предоставляемых реализацией, например, _MSC_EXTENSIONS
. Изменение определения определенных макросов может привести к неопределенному поведению.
Попытка изменить или удалить определенные зарезервированные имена макросов теперь приводит к предупреждению уровня 1 C5308
. В режиме /permissive-
это предупреждение рассматривается как ошибка.
#undef _MSC_EXTENSIONS // Warning C5308: Modifying reserved macro name `_MSC_EXTENSIONS` may cause undefined behavior
Улучшения соответствия в 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++ (C.164) и _com_ptr_t
содержат неявные преобразования как в bool
, так и в Interface*
. Эти два неявных преобразования могут привести к неоднозначности.
Чтобы устранить эту проблему, преобразование в 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
, даже если оно включало вызов функции, объявленной с потенциальной спецификацией исключения. Это формулировка была удалена в C++17, хотя компилятор Microsoft Visual C++ по-прежнему поддерживает её в режиме /permissive
во всех версиях языка C++.
Это /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
Согласно P2286R8, range_formatter
теперь реализовано. Эта функция доступна при компиляции с помощью /std:c++latest
.
Улучшения соответствия в Visual Studio 2022 версии 17.10
Visual Studio 2022 версии 17.10 включает следующие улучшения соответствия, исправления ошибок и изменения поведения в компиляторе Microsoft C/C++.
Подробные сведения об изменениях, внесенных в стандартную библиотеку шаблонов, включая изменения соответствия, исправления ошибок и улучшения производительности, см . в статье 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
Применение _Alignas
к структурированному типу в C
Применяется к языку C (C17 и более поздним версиям). Также добавлено в Microsoft Visual Studio 17.9
В версиях Visual C++ до Visual Studio 2022 версии 17.9, если спецификатор _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
со смещением 32 байта внутри типа struct Outer
.
Это двоичное критическое изменение, поэтому предупреждение теперь создается, когда это изменение вступает в силу. Предупреждение 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++.
Более широкая сводка изменений, внесенных в стандартную библиотеку шаблонов, см. в статье STL Changelog VS 2022 17.9.
Применение _Alignas
к структурированному типу в C
В версиях Visual C++ до Visual Studio 2022 версии 17.9, когда _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
на смещение в 32 байта внутри struct Outer
.
Исправление этого является двоичным критическим изменением, поэтому при применении этого изменения возникает предупреждение. В приведенном выше коде на уровне предупреждения 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 -
<mdspan>
в P0009R18 и последующих изменениях формулировки, которые были применены к стандарту C++23. -
format()
указатели на P2510R3.
Улучшения соответствия в Visual Studio 2022 версии 17.8
Visual Studio 2022 версии 17.8 содержит следующие улучшения соответствия, исправления ошибок и изменения поведения в компиляторе Microsoft C/C++.
/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++.
Добавлен /std:clatest
в компилятор C
Этот параметр ведет себя как /std:c++latest
переключатель для компилятора C++. Этот коммутатор включает все реализованные в настоящее время функции компилятора и стандартной библиотеки, предлагаемые для следующего проекта стандарта C, а также некоторые текущие и экспериментальные функции.
стандартная библиотека C++
Теперь <print>
библиотека поддерживается. См. форматированные выходные данные P2093R14.
Реализовано views::cartesian_product
.
Более широкая сводка изменений, внесенных в стандартную библиотеку шаблонов, см. в статье STL Changelog VS 2022 17.7.
using
соответствие
Ранее директива using
могла привести к тому, что имена из используемых пространств имен оставались видимыми, когда этого не должно было происходить. Это может привести к поиску неквалифицированного имени в пространстве имен, даже если директива не using
активна.
Ниже приведены некоторые примеры нового и старого поведения.
Ссылки в следующих комментариях на "(1)" означают вызов f<K>(t)
в пространстве A
имен:
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!=
типа он обычно подавляет поведение перезаписи. Добавление соответствующего operator!=
является предлагаемым исправлением для кода, ранее скомпилированного в C++17. Дополнительные сведения см. в разделе "Модель программирования".
Улучшения соответствия в Visual Studio 2022 версии 17.4
Visual Studio 2022 версии 17.4 содержит следующие улучшения соответствия, исправления ошибок и изменения поведения в компиляторе Microsoft C/C++.
Базовые типы неклассифицированных enum
без фиксированного типа
В версиях Visual Studio до Visual Studio 2022 версии 17.4 компилятор C++ не правильно определил базовый тип неуправляемого перечисления без фиксированного базового типа. В разделе /Zc:enumTypes
теперь мы правильно реализуем стандартное поведение.
Стандарт C++ требует, чтобы базовый тип enum
был достаточно большим для хранения всех перечислителей в указанном enum
. Перечислители достаточного размера могут задать базовый тип enum
, unsigned int
, long long
или unsigned long long
. Ранее такие enum
типы всегда имели базовый тип int
в компиляторе Майкрософт независимо от значений перечислителя.
При включении этот /Zc:enumTypes
параметр является источником потенциального нарушения двоичной совместимости. Он отключен по умолчанию и не включается с помощью /permissive-
, поскольку исправление может повлиять на бинарную совместимость. Некоторые типы перечисления изменяют размер при включении соответствующего исправления. Некоторые заголовки пакета SDK для Windows включают такие определения перечисления.
Пример
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 до Visual Studio 2022 версии 17.4 компилятор C++ не правильно моделировал типы перечислителей. В перечислениях без фиксированного базового типа он может принять неправильный тип перед закрывающей скобкой. В разделе /Zc:enumTypes
компилятор теперь правильно реализует стандартное поведение.
Стандарт C++ указывает, что в определении перечисления не фиксированного базового типа инициализаторы определяют типы перечислителей. Или для перечислителей без инициализатора по типу предыдущего перечислителя (учет переполнения). Ранее такие перечислители всегда имели выводимый тип перечисления с маркером для базового типа (обычно int
).
При включении этот /Zc:enumTypes
параметр может стать источником несовместимости и вызвать изменения на уровне бинарного кода. Он отключен по умолчанию и не включен посредством /permissive-
, так как исправление может повлиять на бинарную совместимость. Некоторые типы перечисления изменяют размер при включении соответствующего исправления. Некоторые заголовки пакета SDK для Windows включают такие определения перечисления.
Пример
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++.
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 добавлено предупреждение C5255 уровня 3 для незавершенных двунаправленных символов Юникода в комментариях и строках. Предупреждение касается проблемы безопасности, описанной в статье, посвященной программам-троянам (невидимые уязвимости) Николаса Баучера (Nicholas Boucher) и Росса Андерсона (Ross Anderson). Дополнительные сведения о двунаправленных символах Unicode см. в приложении № 9 к стандарту Unicode® с описанием двунаправленного алгоритма Юникода.
Предупреждение C5255 касается только файлов, которые после преобразования содержат двунаправленные символы Unicode. Это предупреждение относится к файлам UTF-8, UTF-16 и UTF-32, поэтому необходимо указать правильную исходную кодировку. Это изменение, нарушающее совместимость исходного кода.
Пример (до и после)
В версиях Visual Studio до Visual Studio 2022 версии 17.2 незавершенный двунаправленный символ не вызывал предупреждения. В 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
тай-брейк
Visual Studio 2022 версии 17.2 исправляет ошибку в <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 без предоставления функций POSIX, когда __STDC__
равно 1
, невозможно определить этот макрос для 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++.
Обнаружение неправильно сформированного захвата по умолчанию в нелокальных лямбда-выражениях
Стандарт C++ разрешает использовать захват по умолчанию только в лямбда-выражениях, находящихся в области видимости блока. В Visual Studio 2022 версии 17.1 и более поздних версиях компилятор обнаруживает, когда по умолчанию запись не разрешена в нелокальном лямбда-выражении. Выдается новое предупреждение уровня 4, C5253.
Это критическое изменение исходного кода. Оно применяется в любом режиме, в котором используется новый лямбда-процессор: /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++.
Предупреждение о ширине битового поля для типа перечисления
Если экземпляр типа перечисления объявляется как битовое поле, его ширина должна вмещать все возможные значения перечисления. В противном случае компилятор выдаст диагностическое сообщение. Рассмотрим следующий пример:
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 №3478 эта ошибка была исправлена. Это изменение реализуется в MSVC. Когда пример компилируется с помощью /permissive-
(и /diagnostics:caret
), выдается следующая ошибка.
t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
return p >= 0;
^
Это поведение компилятора является разрушительным изменением исходного и двоичного кода и влияет на код, скомпилированный с использованием /permissive-
во всех /std
режимах.