Ulepszenia zgodności języka C++, zmiany zachowania i poprawki błędów w programie Visual Studio 2022
Język Microsoft C/C++ w programie Visual Studio (MSVC) wprowadza ulepszenia zgodności i poprawki błędów w każdej wersji. W tym artykule wymieniono znaczące ulepszenia najpierw według głównych wydań, a następnie według wersji. Aby przejść bezpośrednio do zmian dla określonej wersji, użyj linków w tym artykule w górnej części tego artykułu.
Ten dokument zawiera listę zmian w programie Visual Studio 2022.
W przypadku zmian we wcześniejszych wersjach programu Visual Studio:
Wersja | Link ulepszenia zgodności |
---|---|
2019 | Ulepszenia zgodności języka C++ w programie Visual Studio 2019 |
2017 | Ulepszenia zgodności języka C++ w programie Visual Studio 2017 |
2003-2015 | Visual C++ — co nowego od roku 2003 do 2015 |
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.13
Program Visual Studio 2022 w wersji 17.13 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Szczegółowe podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, w tym zmian zgodności, poprawek błędów i ulepszeń wydajności, zobacz STL Changelog VS 2022 17.13.
Zmiana wyszukiwania zależnego od argumentów (ADL)
Konstrukcje językowe, takie jak range-for i powiązania strukturalne, mają specjalne reguły wyszukiwania zależnego od argumentów dla pewnych identyfikatorów, takich jak begin
, end
lub get
. Wcześniej to wyszukiwanie obejmowało kandydatów z std
przestrzeni nazw, nawet jeśli przestrzeń nazw std
nie jest częścią zwykłego zestawu skojarzonych przestrzeni nazw dla wyszukiwania zależnego od argumentów.
Programy, które wprowadziły deklaracje std
dla tych konstrukcji, nie są już kompilowane. Zamiast tego deklaracje powinny znajdować się w normalnej skojarzonej przestrzeni nazw dla zaangażowanych typów (ewentualnie w tym globalnej przestrzeni nazw).
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>'
{
...
}
}
Nie można modyfikować makr zarezerwowanych przez implementację
Wcześniej kompilator zezwolił na zmianę lub niezdefiniowanie niektórych makr dostarczanych przez implementację, takich jak _MSC_EXTENSIONS
. Zmiana definicji niektórych makr może spowodować niezdefiniowane zachowanie.
Próba zmiany lub niezdefiniowania niektórych zastrzeżonych nazw makr powoduje teraz wyświetlenie ostrzeżenia poziomu 1 C5308
. W trybie /permissive-
to ostrzeżenie jest traktowane jako błąd.
#undef _MSC_EXTENSIONS // Warning C5308: Modifying reserved macro name `_MSC_EXTENSIONS` may cause undefined behavior
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.12
Program Visual Studio 2022 w wersji 17.12 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Szczegółowe podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, w tym zmian zgodności, poprawek błędów i ulepszeń wydajności, można znaleźć w temacie STL Changelog VS 2022 17.12.
_com_ptr_t::operator bool()
jest teraz jawne
Jest to zmiana naruszająca zgodność źródła lub binarki.
Niejawna konwersja do bool
z _com_ptr_t
wystąpień może być zaskakująca lub prowadzić do błędów kompilatora. Niejawne funkcje konwersji są odradzane przez Podstawowe wytyczne C++ (C.164), a _com_ptr_t
zawierała niejawne konwersje zarówno do bool
, jak i Interface*
. Te dwie niejawne konwersje mogą prowadzić do niejednoznaczności.
Aby rozwiązać ten problem, konwersja na bool
jest teraz jawna. Konwersja na Interface*
jest niezmieniona.
Makro jest udostępniane w celu rezygnacji z tego nowego zachowania i przywrócenia poprzedniej niejawnej konwersji. Skompiluj za pomocą polecenia /D_COM_DISABLE_EXPLICIT_OPERATOR_BOOL
, aby zrezygnować z tej zmiany. Zalecamy zmodyfikowanie kodu tak, aby nie polegał na niejawnych konwersjach.
Na przykład:
#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'
}
Wyrażenia stałe nie zawsze są noexcept
w trybie permissywnym
Zmiana ta powoduje niezgodność w kodzie źródłowym lub binarnym.
Wyrażenie stałe noexcept
zawsze było, nawet jeśli obejmowało wywołanie funkcji zadeklarowanej ze specyfikacją wyjątków, która może zostać zgłoszona. To sformułowanie zostało usunięte w języku C++17, chociaż kompilator języka Microsoft Visual C++ nadal obsługiwał go w /permissive
trybie we wszystkich wersjach języka C++.
Tryb /permissive
został usunięty. Wyrażenia stałe nie mają już specjalnego domyślnego zachowania.
Specyfikator noexcept
funkcji constexpr
jest teraz uwzględniany we wszystkich trybach. Ta zmiana jest wymagana do poprawnej implementacji późniejszych podstawowych rozwiązań problemów, które opierają się na standardowym noexcept
zachowaniu.
Na przykład:
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.
}
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.11
Program Visual Studio 2022 w wersji 17.11 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Szczegółowe podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, w tym zmian zgodności, poprawek błędów i ulepszeń wydajności, można znaleźć w dokumencie STL Changelog VS 2022 17.11.
Drukuj puste wiersze za pomocą polecenia println
Na P3142R0 można teraz łatwo wygenerować pusty wiersz za pomocą polecenia println
. Ta funkcja jest dostępna podczas kompilowania za pomocą /std:c++latest
.
Przed tą zmianą napisałeś: println("");
Teraz piszesz: println();
.
-
println();
jest odpowiednikiemprintln(stdout);
-
println(FILE* stream);
jest odpowiednikiemprintln(stream, "\n");
Zaimplementowano range_formatter
Zgodnie z P2286R8, range_formatter
jest teraz zaimplementowany. Ta funkcja jest dostępna podczas kompilowania z /std:c++latest
.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.10
Program Visual Studio 2022 w wersji 17.10 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Szczegółowe podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, w tym zmian zgodności, poprawek błędów i ulepszeń wydajności, można znaleźć w temacie STL Changelog VS 2022 17.10.
Specjalizacja operatora konwersji z jawnie określonym typem zwrotnym
Niegdyś kompilator niewłaściwie specjalizował operatorów konwersji, co mogło prowadzić do niezgodnego typu zwrotnego. Te nieprawidłowe specjalizacje już się nie zdarzają. Jest to zmiana łamiąca zgodność w kodzie źródłowym.
// 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
}
Dodano obsługę parametrów #elifdef
i #elifndef
Dodano obsługę WG21 P2334R1 (C++23) i WG14 N2645 (C++23), które wprowadziły dyrektywy preprocesora #elifdef
i #elifndef
.
Wymaga /std:clatest
lub /std:c++latest
.
Przed:
#ifdef __cplusplus
#include <atomic>
#elif !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
#else
#include <custom_atomics_library.h>
#endif
Po:
#ifdef __cplusplus
#include <atomic>
#elifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#else
#include <custom_atomics_library.h>
#endif
Stosowanie typu _Alignas
strukturalnego w języku C
Dotyczy języka C (C17 i nowszych). Dodano również do programu Microsoft Visual Studio 17.9
W wersjach Visual C++ przed Visual Studio 2022 w wersji 17.9, jeśli specyfikator _Alignas
pojawił się obok typu strukturalnego w deklaracji, nie został poprawnie zastosowany zgodnie ze Standardem 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");
Zgodnie ze Standardem ISO-C ten kod powinien zostać skompilowany bez generowania komunikatu diagnostycznego.
Dyrektywa _Alignas
ma zastosowanie tylko do zmiennej składowej member1
. Nie może zmienić wyrównania obiektu struct Inner
. Jednak przed uruchomieniem programu Visual Studio 17.9.1 wyemitowano diagnostykę "nieprawidłowe wyrównanie". Kompilator wyrównał member2
do przesunięcia 32 bajtów w obrębie typu struct Outer
.
Jest to binarna zmiana łamiąca zgodność, więc ostrzeżenie jest teraz generowane, gdy ta zmiana wejdzie w życie. Ostrzeżenie C5274 jest teraz emitowane na poziomie ostrzeżenia 1 dla poprzedniego przykładu: warning C5274: behavior change: _Alignas no longer applies to the type 'Inner' (only applies to declared data objects)
.
Ponadto w poprzednich wersjach programu Visual Studio, gdy _Alignas
specyfikator pojawił się obok deklaracji typu anonimowego, został zignorowany.
// 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");
Wcześniej obie static_assert
instrukcje zakończyły się niepowodzeniem podczas kompilowania tego kodu. Teraz kod jest kompilowany, ale emituje następujące ostrzeżenia poziomu 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)
Aby uzyskać poprzednie zachowanie, zastąp element _Alignas(N)
z __declspec(align(N))
. W przeciwieństwie do _Alignas
, declspec(align)
odnosi się do typu.
Ulepszone ostrzeżenie C4706
Jest to zmiana powodująca niekompatybilność kodu źródłowego. Wcześniej kompilator nie wykrył konwencji zawijania przypisania w nawiasach, jeśli przypisanie było zamierzone, aby pominąć ostrzeżenie C4706 o przypisaniu w wyrażeniu warunkowym. Kompilator wykrywa teraz nawiasy i pomija ostrzeżenie.
#pragma warning(error: 4706)
struct S
{
auto mf()
{
if (value = 9)
return value + 4;
else
return value;
}
int value = 9;
};
Kompilator emituje teraz również ostrzeżenie w przypadkach, gdy funkcja nie jest przywołynięta. Wcześniej, ponieważ mf
jest funkcją śródliniową, do której nie ma odwołania, ostrzeżenie C4706 nie było wyemitowane dla tego kodu. Teraz ostrzeżenie jest emitowane:
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
Aby naprawić to ostrzeżenie, użyj operatora równości, value == 9
, jeśli to jest zamierzone. Albo ująć przypisanie w nawiasy, (value = 9)
, jeśli takie przypisanie jest zamierzone. W przeciwnym razie, ponieważ funkcja nie jest wnioskowana, usuń ją.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.9
Program Visual Studio 2022 w wersji 17.9 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Aby uzyskać szersze podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, zobacz STL Changelog VS 2022 17.9.
Zastosowanie strukturalnego typu _Alignas
w języku C
W wersjach Visual C++ sprzed Visual Studio 2022, wersja 17.9, kiedy _Alignas
pojawiało się obok typu struktury w deklaracji, nie zastosowano go poprawnie zgodnie ze standardem ISO-C. Na przykład:
// 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");
Zgodnie ze standardem ISO-C ten kod powinien zostać skompilowany bez static_assert
emitowania diagnostyki. Dyrektywa _Alignas
ma zastosowanie tylko do zmiennej składowej member1
. Nie może zmienić wyrównania obiektu struct Inner
. Jednak przed wydaniem 17.9.1 programu Visual Studio emitowano diagnostykę "nieprawidłowe wyrównanie". Kompilator wyrównał member2
do przesunięcia o 32 bajty wewnątrz struct Outer
.
Naprawienie tej zmiany jest zmianą powodującą niezgodność binarną, więc po zastosowaniu tej zmiany zachowania jest emitowane ostrzeżenie. W poprzednim kodzie ostrzeżenie C5274 "_Alignas
nie ma już zastosowania do typu "Inner" (dotyczy tylko zadeklarowanych obiektów danych)" jest teraz emitowane na poziomie ostrzeżenia 1.
W poprzednich wersjach programu Visual Studio _Alignas
była ignorowana, gdy była wyświetlana obok deklaracji typu anonimowego. Na przykład:
// 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");
Wcześniej obie static_assert
instrukcje zakończyły się niepowodzeniem podczas kompilowania tego kodu. Kod jest teraz kompilowany, ale z następującymi ostrzeżeniami poziomu 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)
Jeśli chcesz użyć wcześniejszego zachowania, zastąp _Alignas(N)
__declspec(align(N))
. W przeciwieństwie do _Alignas
, declspec(align)
można zastosować do typu.
__VA_OPT__
jest włączona jako rozszerzenie w obszarze /Zc:preprocessor
__VA_OPT__
dodano do języków C++20 i C23. Przed jego dodaniem nie było standardowego sposobu, aby pominąć przecinek w makrze funkcyjnym. Aby zapewnić lepszą zgodność z poprzednimi wersjami, __VA_OPT__
jest włączony w ramach preprocesora /Zc:preprocessor
opartego na tokenach we wszystkich wersjach językowych.
Na przykład teraz kompiluje się bez błędu:
#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")
Język C23
W przypadku języka C23 podczas korzystania z przełącznika kompilatora /std:clatest
są dostępne następujące elementy:
Dostępne są następujące wersje dla wszystkich wersji językowych języka C:
Standardowa biblioteka C++
Funkcje języka C++23
-
formattable
,range_format
,format_kind
iset_debug_format()
w ramach P2286R8 zakresów formatowania -
<mdspan>
dla P0009R18 i kolejnych zmian sformułowań, które zostały zastosowane do standardu C++23. -
format()
wskaźniki na P2510R3.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.8
Program Visual Studio 2022 w wersji 17.8 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
/FU
generuje błąd
Kompilator języka C akceptował opcję /FU
, chociaż od pewnego czasu nie obsługuje kompilacji zarządzanej. Teraz występuje błąd. Projekty, które przechodzą tę opcję, muszą ograniczyć ją tylko do projektów C++/CLI.
Standardowa biblioteka C++
Moduły nazwane C++23 std
i std.compat
są teraz dostępne podczas kompilowania za pomocą /std:c++20
.
Aby uzyskać szersze podsumowanie zmian wprowadzonych w standardowej bibliotece języka C++, zobacz STL Changelog VS 2022 17.8.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.7
Program Visual Studio 2022 w wersji 17.7 zawiera następujące wyróżnione ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Dodano /std:clatest
do kompilatora języka C
Ten przełącznik zachowuje się jak /std:c++latest
przełącznik dla kompilatora języka C++. Przełącznik włącza wszystkie obecnie zaimplementowane funkcje kompilatora i biblioteki standardowej proponowane dla następnego standardu C, a także niektóre funkcje w toku i eksperymentalne.
Standardowa biblioteka C++
Teraz obsługiwana jest biblioteka <print>
. Zobacz P2093R14 Sformatowane dane wyjściowe.
Zaimplementowano views::cartesian_product
.
Aby uzyskać szersze podsumowanie zmian wprowadzonych w standardowej bibliotece szablonów, zobacz STL Changelog VS 2022 17.7.
using
Zgodność
using
Wcześniej dyrektywa mogła spowodować, że nazwy z używanych przestrzeni nazw pozostaną widoczne, gdy nie powinny. Może to spowodować, że niekwalifikowane wyszukiwanie nazw znajdzie nazwę w przestrzeni nazw nawet wtedy, gdy nie ma aktywnej dyrektywy using
.
Oto kilka przykładów nowego i starego zachowania.
Odwołania do "(1)" w poniższych komentarzach oznaczają wywołanie f<K>(t)
w przestrzeni nazw 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>());
}
}
Ten sam podstawowy problem może spowodować odrzucenie kodu, który został wcześniej skompilowany:
#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;
}
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.6
Program Visual Studio 2022 w wersji 17.6 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Przypisania złożone volatile
nie są już przestarzałe
C++20 zniechęca do stosowania niektórych operatorów na typach kwalifikowanych przez volatile
. Na przykład po skompilowaniu następującego kodu za pomocą cl /std:c++20 /Wall test.cpp
polecenia:
void f(volatile int& expr)
{
++expr;
}
Kompilator generuje element test.cpp(3): warning C5214: applying '++' to an operand with a volatile qualified type is deprecated in C++20
.
W języku C++20 operatory złożonego przypisania (operatory w formie @=
) zostały oznaczone jako przestarzałe. W C++23 operatory złożone, które zostały wykluczone w C++20, nie są już uznawane za przestarzałe. Na przykład w języku C++23 następujący kod nie generuje ostrzeżenia, natomiast w języku C++20:
void f(volatile int& e1, int e2)
{
e1 += e2;
}
Aby uzyskać więcej informacji na temat tej zmiany, zobacz CWG:2654
Ponowne zapisywanie równości w wyrażeniach jest mniejszą zmianą łamiącą zgodność (P2468R2)
W języku C++20 P2468R2 zmienił kompilator tak, aby akceptował kod, taki jak:
struct S
{
bool operator==(const S&);
bool operator!=(const S&);
};
bool b = S{} != S{};
Kompilator akceptuje ten kod, co oznacza, że kompilator jest bardziej rygorystyczny z kodem, takim jak:
struct S
{
operator bool() const;
bool operator==(const S&);
};
bool b = S{} == S{};
Wersja 17.5 kompilatora akceptuje ten program. Wersja 17.6 kompilatora go odrzuca. Aby rozwiązać ten problem, dodaj const
do operator==
, aby usunąć niejednoznaczność. Możesz też dodać odpowiadający operator!=
do definicji, co pokazano w poniższym przykładzie:
struct S
{
operator bool() const;
bool operator==(const S&);
bool operator!=(const S&);
};
bool b = S{} == S{};
Kompilator Microsoft C/C++ w wersjach 17.5 i 17.6 akceptuje poprzedni program i wywołuje S::operator==
w obu wersjach.
Ogólny model programowania opisany w P2468R2 polega na tym, że jeśli istnieje odpowiadający element operator!=
dla typu, zazwyczaj tłumi zachowanie ponownego zapisywania. Dodanie odpowiadającego mu operator!=
jest sugerowaną poprawką dla kodu, który został wcześniej skompilowany z użyciem C++17. Aby uzyskać więcej informacji, zobacz Model programowania.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.4
Program Visual Studio 2022 w wersji 17.4 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Podstawowe typy niezakresowe enum
bez stałego typu
W wersjach programu Visual Studio przed programem Visual Studio 2022 w wersji 17.4 kompilator języka C++ nie określił poprawnie bazowego typu wyliczenia niezakresowego bez stałego typu podstawowego. W obszarze /Zc:enumTypes
, teraz poprawnie implementujemy standardowe zachowanie.
Język C++ Standard wymaga, aby podstawowy typ obiektu enum
był wystarczająco duży, aby przechowywać wszystkie moduły wyliczające w tym obiekcie enum
. Wystarczająco duże moduły wyliczania mogą ustawić bazowy typ enum
elementu na unsigned int
, long long
lub unsigned long long
. Wcześniej takie enum
typy zawsze miały podstawowy typ int
w kompilatorze firmy Microsoft, niezależnie od wartości modułu wyliczającego.
Po włączeniu opcji /Zc:enumTypes
może dojść do potencjalnej zmiany powodującej niezgodność w kodzie źródłowym i danych binarnych. Jest domyślnie wyłączona i nie jest włączona przez /permissive-
, ponieważ poprawka może wpłynąć na zgodność binarną. Niektóre typy wyliczeniowe zmieniają rozmiar po włączeniu poprawki zgodności. Niektóre nagłówki zestawu Windows SDK zawierają takie definicje wyliczenia.
Przykład
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
};
Typy modułów wyliczania w enum
definicji bez stałego typu bazowego
W wersjach programu Visual Studio przed programem Visual Studio 2022 w wersji 17.4 kompilator języka C++ nie poprawnie modeluje typów modułów wyliczania. Przed zamykającym nawiasem klamrowym wyliczenia, w wyliczeniach bez stałego typu bazowego, może zostać przyjęty nieprawidłowy typ. W obszarze /Zc:enumTypes
kompilator prawidłowo implementuje standardowe zachowanie.
Standard C++ określa, że w definicji wyliczenia bez stałego typu bazowego, inicjatory określają typy enumeratorów. Lub dla modułów wyliczania bez inicjatora, według typu poprzedniego modułu wyliczającego (ewidencjonowanie przepełnienia). Wcześniej takie wyliczniki zawsze miały typ wyliczenia z symbolem zastępczym dla typu bazowego (zazwyczaj int
).
Po włączeniu opcji /Zc:enumTypes
może dojść do potencjalnej zmiany powodującej niezgodność w kodzie źródłowym i w plikach binarnych. Jest ona domyślnie wyłączona i nie jest włączana przez /permissive-
, ponieważ poprawka może mieć wpływ na zgodność binarną. Niektóre typy wyliczeniowe zmieniają rozmiar po włączeniu poprawki zgodności. Niektóre nagłówki zestawu Windows SDK zawierają takie definicje wyliczenia.
Przykład
enum Enum {
A = 'A',
B = sizeof(A)
};
static_assert(B == 1); // previously failed, now succeeds under /Zc:enumTypes
W tym przykładzie moduł A
wyliczający powinien mieć typ char
przed zamykającym nawiasem klamrowym wyliczenia, więc B
należy zainicjować przy użyciu polecenia sizeof(char)
. Przed poprawką /Zc:enumTypes
miał typ wyliczenia A
z wywiedzionym typem bazowym int
, a B
został zainicjowany przy użyciu sizeof(Enum)
lub 4.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.3
Program Visual Studio 2022 w wersji 17.3 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
C: Ulepszone sprawdzanie zgodności modyfikatora między wskaźnikami
Kompilator języka C nie porównywał prawidłowo modyfikatorów między wskaźnikami, zwłaszcza void*
. Ta usterka może spowodować nieprawidłową diagnozę niezgodności między const int**
a void*
oraz zgodności między int* volatile*
a void*
.
Przykład
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
}
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.2
Program Visual Studio 2022 w wersji 17.2 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Nieokreślone ostrzeżenia o znakach dwukierunkowych
Program Visual Studio 2022 w wersji 17.2 dodaje ostrzeżenie poziomu 3 C5255 dla nieokreślonych znaków dwukierunkowych Unicode w komentarzach i ciągach. Ostrzeżenie dotyczy problemu bezpieczeństwa opisanego w Trojan Source: Invisible Vulnerabilities autorstwa Nicholasa Bouchera i Rossa Andersona. Aby uzyskać więcej informacji na temat znaków dwukierunkowych Unicode, zobacz Unicode Standard Annex #9: UNICODE BIDIRECTIONAL ALGORITHM (Załącznik standardowy Unicode® nr 9: ALGORYTM DWUKIERUNKOWY UNICODE).
Ostrzeżenie C5255 dotyczy tylko plików, które po konwersji zawierają znaki dwukierunkowe Unicode. To ostrzeżenie dotyczy plików UTF-8, UTF-16 i UTF-32, więc należy podać odpowiednie kodowanie źródłowe. Ta zmiana jest zmianą powodującą niezgodność źródła.
Przykład (przed/po)
W wersjach programu Visual Studio przed programem Visual Studio 2022 w wersji 17.2 nieokreślony znak dwukierunkowy nie wygenerował ostrzeżenia. Program Visual Studio 2022 w wersji 17.2 generuje ostrzeżenie 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
dogrywka
Program Visual Studio 2022 w wersji 17.2 naprawia usterkę w <charconv>
from_chars()
float
regułach tiebreaker, które wygenerowały nieprawidłowe wyniki. Ta usterka dotyczyła ciągów dziesiętnych, które znajdowały się dokładnie w połowie kolejnych float
wartości w wąskim zakresie. (Najmniejsze i największe wartości, których dotyczy problem, to 32768.009765625
i 131071.98828125
, odpowiednio). Zasada rozstrzygania remisów chciała zaokrąglić do liczby parzystej, a liczba parzysta okazała się być mniejsza, ale implementacja niepoprawnie zaokrągliła w górę (double
pozostała niezmieniona). Aby uzyskać więcej informacji i szczegóły dotyczące implementacji, zobacz microsoft/STL#2366.
Ta zmiana wpływa na zachowanie środowiska uruchomieniowego w określonym zakresie przypadków:
Przykład
// 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");
}
W wersjach przed programem Visual Studio 2022 w wersji 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.
W programie Visual Studio 2022 w wersji 17.2 i po:
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__
udostępnia __STDC__
dla C
Standard C wymaga, aby zgodna implementacja języka C definiuje __STDC__
jako 1
. Ze względu na zachowanie UCRT, które nie ujawnia funkcji POSIX, gdy __STDC__
jest 1
, nie jest możliwe zdefiniowanie tego makra dla języka C domyślnie bez wprowadzania zmian prowadzących do niekompatybilności ze stabilnymi wersjami języka. Program Visual Studio 2022 w wersji 17.2 lub nowszej dodaje opcję /Zc:__STDC__
zgodności, która definiuje to makro. Nie ma negatywnej wersji opcji. Obecnie planujemy domyślnie używać tej opcji dla przyszłych wersji języka C.
Ta zmiana łamie zgodność ze źródłem. Ma zastosowanie, gdy tryb C11 lub C17 jest włączony (/std:c11
lub /std:c17
) i /Zc:__STDC__
jest określony.
Przykład
// 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__
*/
Ostrzeżenie dotyczące brakujących nawiasów klamrowych
Ostrzeżenie C5246 zgłasza brak nawiasów klamrowych podczas inicjalizacji agregatu podobiektu. Przed wydaniem Visual Studio 2022 w wersji 17.2 ostrzeżenie nie uwzględniało kwestii anonimowego struct
lub union
.
Ta zmiana jest zmianą powodującą niezgodność źródła. Ma zastosowanie, gdy jest włączone ostrzeżenie off-by-default C5246.
Przykład
W programie Visual Studio 2022 w wersji 17.2 lub nowszej ten kod powoduje teraz błąd:
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
*/
Aby rozwiązać ten problem, dodaj nawiasy klamrowe do inicjatora:
void f()
{
S s = { { 1.0f, 2.0f, 3.14f, 4.0f } };
}
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.1
Program Visual Studio 2022 w wersji 17.1 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Wykryć niepoprawną domyślną wartość przechwytywania w nielokalnych wyrażeniach lambda
Standard C++ zezwala tylko, aby wyrażenie lambda w zakresie bloku miało domyślne przechwytywanie. W programie Visual Studio 2022 w wersji 17.1 lub nowszej kompilator wykrywa, gdy ustawienie domyślne przechwytywania nie jest dozwolone w nielokalnym wyrażeniu lambda. Emituje nowe ostrzeżenie poziomu 4, C5253.
Ta zmiana jest zmianą powodującą niezgodność w kodzie źródłowym. Ma zastosowanie w dowolnym trybie, który używa nowego procesora lambda: /Zc:lambda
, /std:c++20
lub /std:c++latest
.
Przykład
W programie Visual Studio 2022 w wersji 17.1 ten kod generuje teraz błąd:
#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; };
// ^
Aby rozwiązać ten problem, usuń ustawienie domyślne przechwytywania:
#pragma warning(error:5253)
auto incr = [](int value) { return value + 1; };
C4028 jest teraz C4133 dla operacji konwersji funkcji na wskaźnik
Przed programem Visual Studio 2022 w wersji 17.1 kompilator zgłosił niepoprawny komunikat o błędzie w niektórych porównaniach wskaźników do funkcji w kodzie języka C. Niepoprawny komunikat został zgłoszony podczas porównywania dwóch wskaźników funkcji, które miały te same liczby argumentów, ale niezgodne typy. Teraz wydajemy inne ostrzeżenie, które informuje o niezgodności wskaźnika do funkcji, a nie o niezgodności parametrów funkcji.
Ta zmiana jest zmianą niezgodną ze źródłem. Ma zastosowanie, gdy kod jest kompilowany jako C.
Przykład
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)'
Błąd dotyczący niepowiązanego static_assert
W programie Visual Studio 2022 w wersji 17.1 lub nowszej, jeśli wyrażenie skojarzone z static_assert
nie jest wyrażeniem zależnym, kompilator oblicza wyrażenie w trakcie jego parsowania. Jeśli wyrażenie zwróci wartość false
, kompilator zgłasza błąd. Wcześniej, jeśli element static_assert
znajdował się w treści szablonu funkcji (lub w treści funkcji składowej szablonu klasy), kompilator nie wykonałby tej analizy.
Ta zmiana powoduje niekompatybilność źródła. Ma zastosowanie w dowolnym trybie, który implikuje /permissive-
lub /Zc:static_assert
. Tę zmianę zachowania można wyłączyć przy użyciu opcji kompilatora /Zc:static_assert-
.
Przykład
W programie Visual Studio 2022 w wersji 17.1 lub nowszej ten kod powoduje teraz błąd:
template<typename T>
void f()
{
static_assert(false, "BOOM!");
}
Aby rozwiązać ten problem, spraw, aby wyrażenie było zależne. Na przykład:
template<typename>
constexpr bool dependent_false = false;
template<typename T>
void f()
{
static_assert(dependent_false<T>, "BOOM!");
}
Dzięki tej zmianie kompilator emituje błąd tylko wtedy, gdy szablon funkcji f
zostanie zainicjowany.
Ulepszenia zgodności w programie Visual Studio 2022 w wersji 17.0
Program Visual Studio 2022 w wersji 17.0 zawiera następujące ulepszenia zgodności, poprawki błędów i zmiany zachowania w kompilatorze microsoft C/C++.
Ostrzeżenie dotyczące szerokości pola bitowego dla typu wyliczenia
W przypadku deklarowania wystąpienia typu wyliczenia jako pola bitowego szerokość pola bitowego musi pomieścić wszystkie możliwe wartości wyliczenia. W przeciwnym razie kompilator wysyła komunikat diagnostyczny. Rozważmy następujący przykład: Rozważ następujące kwestie:
enum class E : unsigned { Zero, One, Two };
struct S {
E e : 1;
};
Programista może oczekiwać, że składowa S::e
klasy będzie przechowywać dowolną jawnie nazwaną enum
wartość. Biorąc pod uwagę liczbę elementów wyliczenia, nie jest to możliwe. Pole bitowe nie może obejmować zakresu jawnie podanych wartości E
(koncepcyjnie domenyE
). Aby rozwiązać problem, że szerokość pola bitowego nie jest wystarczająco duża dla domeny wyliczenia, nowe (domyślnie wyłączone) ostrzeżenie jest dodawane do 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 };
^
To zachowanie kompilatora to zmiana łamiąca zgodność źródłowa i binarna, która ma wpływ na wszystkie tryby /std
i /permissive
.
Podczas porównywania uporządkowanego wskaźnika względem nullptr
lub 0 występuje błąd.
Standard C++ przypadkowo zezwalał na uporządkowane porównanie wskaźników względem nullptr
lub 0. Na przykład:
bool f(int *p)
{
return p >= 0;
}
WG21 dokument N3478 usunął to przeoczenie. Ta zmiana jest implementowana w MSVC. Gdy przykład jest kompilowany przy użyciu metody /permissive-
(i /diagnostics:caret
), emituje następujący błąd:
t.cpp(3,14): error C7664: '>=': ordered comparison of pointer and integer zero ('int *' and 'int')
return p >= 0;
^
To zachowanie kompilatora to zmiana prowadząca do niekompatybilności na poziomie kodu źródłowego i binarnego, która ma wpływ na kod skompilowany przy użyciu /permissive-
we wszystkich trybach /std
.