Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Microsoft C/C++ i Visual Studio (MSVC) gör överensstämmelseförbättringar och felkorrigeringar i varje version. I den här artikeln listas förbättringarna efter större utgåva, sedan efter version. Om du vill gå direkt till ändringarna för en viss version använder du listan nedan I den här artikeln.
I det här dokumentet visas ändringarna i Visual Studio 2019. En guide till ändringarna i Visual Studio 2022 finns i Förbättringar av C++-överensstämmelse i Visual Studio 2022. Ändringar i Visual Studio 2017 finns i Förbättringar av C++-överensstämmelse i Visual Studio 2017. En fullständig lista över tidigare överensstämmelseförbättringar finns i Visual C++ Nyheter 2003 till och med 2015.
Anpassningsförbättringar i Visual Studio 2019 RTW (version 16.0)
Visual Studio 2019 RTW innehåller följande överensstämmelseförbättringar, felkorrigeringar och beteendeändringar i Microsoft C++-kompilatorn.
Anmärkning
C++20-funktioner var endast tillgängliga i /std:c++latest läge i Visual Studio 2019 tills C++20-implementeringen ansågs vara klar. Visual Studio 2019 version 16.11 introducerar /std:c++20 kompilatorläget. I den här artikeln fungerar funktioner som ursprungligen krävde /std:c++latest läge nu i /std:c++20 läge eller senare i de senaste versionerna av Visual Studio. Vi har uppdaterat dokumentationen för att nämna /std:c++20, även om det här alternativet inte var tillgängligt när funktionerna först släpptes.
Förbättrat modulstöd för mallar och felidentifiering
Modulerna finns nu officiellt i C++20-standarden. Förbättrat stöd har lagts till i Visual Studio 2017 version 15.9. Mer information finns i Bättre mallstöd och felidentifiering i C++-moduler med MSVC 2017 version 15.9.
Ändrad specifikation av aggregeringstyp
Specifikationen för en aggregeringstyp har ändrats i C++20 (se Förhindra aggregeringar med användardefinierade konstruktorer). I Visual Studio 2019 under /std:c++latest (eller /std:c++20 i Visual Studio 2019 version 16.11 och senare) är en klass med någon användardeklarerad konstruktor (till exempel en konstruktor deklarerad = default eller = delete) inte en aggregering. Tidigare skulle endast konstruktorer som tillhandahålls av användaren diskvalificera en klass från att vara en aggregering. Den här ändringen medför fler begränsningar för hur sådana typer kan initieras.
Följande kod kompileras utan fel i Visual Studio 2017 men genererar felen C2280 och C2440 i Visual Studio 2019 under /std:c++20 eller /std:c++latest:
struct A
{
    A() = delete; // user-declared ctor
};
struct B
{
    B() = default; // user-declared ctor
    int i = 0;
};
A a{}; // ill-formed in C++20, previously well-formed
B b = { 1 }; // ill-formed in C++20, previously well-formed
Partiellt stöd för operator <=>
              P0515R3 C++20 introducerar trevägsjämförelseoperatorn <=> , även kallad "rymdskeppsoperator". Visual Studio 2019 version 16.0 i /std:c++latest-läge introducerar partiellt stöd för operatorn genom att uppmärksamma fel för syntax som nu inte tillåts. Följande kod kompileras till exempel utan fel i Visual Studio 2017 men genererar flera fel i Visual Studio 2019 under /std:c++20 eller /std:c++latest:
struct S
{
    bool operator<=(const S&) const { return true; }
};
template <bool (S::*)(const S&) const>
struct U { };
int main(int argc, char** argv)
{
    U<&S::operator<=> u; // In Visual Studio 2019 raises C2039, 2065, 2146.
}
Undvik felen genom att infoga ett blanksteg i den felande linjen före den sista vinkelparentesen: U<&S::operator<= > u;.
Referenser till typer med felmatchade cv-kvalificerare
Anmärkning
Den här ändringen påverkar endast Visual Studio 2019-versionerna 16.0 till och med 16.8. Den återställdes från och med Visual Studio 2019 version 16.9
Tidigare tillät MSVC direktbindning av en referens från en typ med felmatchade cv-kvalificerare under den översta nivån. Den här bindningen kan göra det möjligt att ändra förmodade const-data som referensen hänvisar till.
Kompilatorn för Visual Studio 2019 version 16.0 till 16.8 skapar i stället en tillfällig, vilket krävdes av standarden vid den tidpunkten. Senare ändrades standarden retroaktivt, vilket gjorde att det tidigare beteendet i Visual Studio 2017 och äldre blev rätt, medan beteendet för Visual Studio 2019 version 16.0 till 16.8 blev felaktigt. Den här ändringen har därför återställts med början i Visual Studio 2019 version 16.9.
Se Liknande typer och referensbindning för en relaterad ändring.
I Visual Studio 2017 kompileras till exempel följande kod utan varningar. I Visual Studio 2019 version 16.0 till 16.8 varnar kompilatorn C4172. Från och med Visual Studio 2019 version 16.9 kompileras koden återigen utan varningar:
struct X
{
    const void* const& PData() const
    {
        return _pv;
    }
    void* _pv;
};
int main()
{
    X x;
    auto p = x.PData(); // C4172 <func:#1 "?PData@X@@QBEABQBXXZ"> returning address of local variable or temporary
}
              reinterpret_cast från en överbelastad funktion
Argumentet till reinterpret_cast är inte en av de kontexter där adressen till en överbelastad funktion är tillåten. Följande kod kompileras utan fel i Visual Studio 2017, men i Visual Studio 2019 uppstår felet C2440:
int f(int) { return 1; }
int f(float) { return .1f; }
using fp = int(*)(int);
int main()
{
    fp r = reinterpret_cast<fp>(&f); // C2440: cannot convert from 'overloaded-function' to 'fp'
}
Undvik felet genom att använda en tillåten omvandling för det här scenariot.
int f(int);
int f(float);
using fp = int(*)(int);
int main()
{
    fp r = static_cast<fp>(&f); // or just &f;
}
Lambda-stängningar
I C++14 är lambda-stängningstyper inte literaler. Den primära konsekvensen av den här regeln är att en lambda kanske inte tilldelas till en constexpr variabel. Följande kod kompileras utan fel i Visual Studio 2017, men i Visual Studio 2019 uppstår felet C2127:
int main()
{
    constexpr auto l = [] {}; // C2127 'l': illegal initialization of 'constexpr' entity with a non-constant expression
}
Undvik felet genom att antingen ta bort kvalificeraren constexpr eller ändra överensstämmelseläget till /std:c++17 eller senare.
              std::create_directory felkoder
Implementerade P1164 från C++20 villkorslöst. Detta ändrar std::create_directory för att kontrollera om målet redan är en katalog vid ett misslyckande. Tidigare omvandlades alla ERROR_ALREADY_EXISTS-fel till koder som indikerade framgång men inte skapade katalogen.
operator<<(std::ostream, nullptr_t)
Enligt LWG 2221 har operator<<(std::ostream, nullptr_t) lagts till för att skriva nullptr till strömmar.
Fler parallella algoritmer
Nya parallella versioner av is_sorted, is_sorted_until, is_partitioned, set_difference, set_intersection, is_heapoch is_heap_until.
Fixar vid atomär initialisering
              P0883 "Åtgärda atomisk initiering" ändrar std::atomic för att värdeinitiera det innehållna T snarare än att standardinitiera det. Korrigeringen aktiveras när du använder Clang/LLVM med Microsofts standardbibliotek. Den är för närvarande inaktiverad för Microsoft C++-kompilatorn som en lösning för en bugg i constexpr bearbetningen.
              remove_cvref och remove_cvref_t
              remove_cvref och remove_cvref_t typdragen implementerades från P0550. Dessa tar bort referensegenskaper och cv-behörighet från en typ utan att omvandla funktioner och arrays till pekare (till skillnad från std::decay och std::decay_t).
Funktionstestmakro
              P0941R2 – funktionstestmakron har slutförts med stöd för __has_cpp_attribute. Funktionstestmakron stöds i alla standardlägen.
Förhindra aggregeringar med användardefinierade konstruktorer
C++20 P1008R1 – ett förbud mot aggregeringar med användardefinierade konstruktorer är slutfört.
              reinterpret_cast i en constexpr funktion
A reinterpret_cast är ogiltigt i en constexpr funktion. Microsoft C++-kompilatorn skulle tidigare endast avvisa reinterpret_cast om den användes i en constexpr kontext. I Visual Studio 2019, i alla språkstandardlägen, diagnostiserar kompilatorn korrekt en reinterpret_cast i definitionen av en constexpr funktion. Följande kod genererar nu C3615:
long long i = 0;
constexpr void f() {
    int* a = reinterpret_cast<int*>(i); // C3615: constexpr function 'f' cannot result in a constant expression
}
Undvik felet genom att ta bort constexpr modifieraren från funktionsdeklarationen.
Rätt diagnostik för basic_string intervallkonstruktor
I Visual Studio 2019 undertrycker inte längre intervallkonstruktorn kompilatordiagnostiken med basic_string. Följande kod kompileras utan varningar i Visual Studio 2017, trots eventuell förlust av data från wchar_t till char när du initierar out:
std::wstring ws = /* . . . */;
std::string out(ws.begin(), ws.end()); // VS2019 C4244: 'argument': conversion from 'wchar_t' to 'const _Elem', possible loss of data.
Visual Studio 2019 genererar korrekt varning C4244. För att undvika varningen kan du initiera std::string som visat i det här exemplet:
std::wstring ws = L"Hello world";
std::string out;
for (wchar_t ch : ws)
{
    out.push_back(static_cast<char>(ch));
}
Felaktiga anrop till += och -= under /clr eller /ZW identifieras nu korrekt
En bugg introducerades i Visual Studio 2017 som gjorde att kompilatorn tyst ignorerade fel och inte genererade någon kod för ogiltiga anrop till += och -= under /clr eller /ZW. Följande kod kompileras utan fel i Visual Studio 2017 men i Visual Studio 2019 uppstår felet C2845 korrekt:
public enum class E { e };
void f(System::String ^s)
{
    s += E::e; // in VS2019 C2845: 'System::String ^': pointer arithmetic not allowed on this type.
}
Undvik felet i det här exemplet genom att använda operatorn += med ToString() metoden : s += E::e.ToString();.
Initierare för infogade statiska datamedlemmar
Ogiltiga medlemsåtkomster inom inline och static constexpr initialiseringar identifieras nu korrekt. I följande exempel kompileras utan fel i Visual Studio 2017, men i Visual Studio 2019 under /std:c++17 läge eller senare uppstår felet C2248:
struct X
{
    private:
        static inline const int c = 1000;
};
struct Y : X
{
    static inline int d = c; // VS2019 C2248: cannot access private member declared in class 'X'.
};
För att undvika felet deklarerar du medlemmen X::c som skyddad:
struct X
{
    protected:
        static inline const int c = 1000;
};
C4800 har återställts
MSVC brukade ha en prestandavarning C4800 om implicit konvertering till bool. Det var för bullrigt och kunde inte undertryckas, vilket ledde till att vi tar bort det i Visual Studio 2017. Men under livscykeln för Visual Studio 2017 fick vi massor av feedback om de användbara fall som det löste. Vi tar tillbaka en noggrant skräddarsydd C4800 i Visual Studio 2019 tillsammans med den förklarande C4165. Båda dessa varningar är lätta att undertrycka: antingen genom att använda en explicit gjutning eller genom att jämföra med 0 av lämplig typ. C4800 är en nivå 4-varning som är avstängd som standard, och C4165 är en nivå 3-varning som är avstängd som standard. Båda kan identifieras med hjälp av kompilatoralternativet /Wall .
I följande exempel höjs C4800 och C4165 under /Wall:
bool test(IUnknown* p)
{
    bool valid = p; // warning C4800: Implicit conversion from 'IUnknown*' to bool. Possible information loss
    IDispatch* d = nullptr;
    HRESULT hr = p->QueryInterface(__uuidof(IDispatch), reinterpret_cast<void**>(&d));
    return hr; // warning C4165: 'HRESULT' is being converted to 'bool'; are you sure this is what you want?
}
Om du vill undvika varningarna i föregående exempel kan du skriva koden så här:
bool test(IUnknown* p)
{
    bool valid = p != nullptr; // OK
    IDispatch* d = nullptr;
    HRESULT hr = p->QueryInterface(__uuidof(IDispatch), reinterpret_cast<void**>(&d));
    return SUCCEEDED(hr);  // OK
}
Den lokala klassmedlemsfunktionen har ingen kropp
I Visual Studio 2017 utlöses varning C4822 endast när kompilatoralternativet /w14822 uttryckligen anges. Den visas inte med /Wall. I Visual Studio 2019 är C4822 en off-by-default-varning , vilket gör att den kan identifieras under /Wall utan att uttryckligen behöva anges /w14822 .
void example()
{
    struct A
        {
            int boo(); // warning C4822: Local class member function doesn't have a body
        };
}
Funktionsmallskroppar som innehåller if constexpr satser
I Visual Studio 2019 under /std:c++20 eller /std:c++latest, har mallfunktionskroppar, som har if constexpr instruktioner, extra parsningsrelaterade kontroller aktiverade. I Visual Studio 2017 genererar till exempel följande kod endast C7510 om alternativet /permissive- har angetts. I Visual Studio 2019 genererar samma kod fel även när alternativet /permissive har angetts:
// C7510.cpp
// compile using: cl /EHsc /W4 /permissive /std:c++latest C7510.cpp
#include <iostream>
template <typename T>
int f()
{
    T::Type a; // error C7510: 'Type': use of dependent type name must be prefixed with 'typename'
    // To fix the error, add the 'typename' keyword. Use this declaration instead:
    // typename T::Type a;
    if constexpr (a.val)
    {
        return 1;
    }
    else
    {
        return 2;
    }
}
struct X
{
    using Type = X;
    constexpr static int val = 1;
};
int main()
{
    std::cout << f<X>() << "\n";
}
Undvik felet genom att lägga till nyckelordet typename i deklarationen av a: typename T::Type a;.
Inbyggd assemblerkod stöds inte i ett lambdauttryck
Microsoft C++-teamet har nyligen blivit medvetna om ett säkerhetsproblem där användningen av inline-assembler inom en lambda kan leda till att ebp (returadressregistret) skadas vid körning. En angripare kan eventuellt dra nytta av det här scenariot. Den inline assemblerare stöds endast på x86 och interaktionen mellan den inline assembleraren och resten av kompilatorn är dålig. Med tanke på dessa fakta och problemets art var den säkra lösningen att inte tillåta inline assembler i ett lambda-uttryck.
Den enda användningen av inbyggd assembler i ett lambda-uttryck som vi har hittat i praktiken var att fånga returadressen. I det här scenariot kan du samla in returadressen på alla plattformar helt enkelt med hjälp av en inbyggd _ReturnAddress()kompilator .
Följande kod genererar C7553 i Visual Studio 2017 15.9 och senare versioner av Visual Studio:
#include <cstdio>
int f()
{
    int y = 1724;
    int x = 0xdeadbeef;
    auto lambda = [&]
    {
        __asm {  // C7553: inline assembler is not supported in a lambda
            mov eax, x
            mov y, eax
        }
    };
    lambda();
    return y;
}
Undvik felet genom att flytta sammansättningskoden till en namngiven funktion enligt följande exempel:
#include <cstdio>
void g(int& x, int& y)
{
    __asm {
        mov eax, x
        mov y, eax
    }
}
int f()
{
    int y = 1724;
    int x = 0xdeadbeef;
    auto lambda = [&]
    {
        g(x, y);
    };
    lambda();
    return y;
}
int main()
{
    std::printf("%d\n", f());
}
Iterator-felsökning och std::move_iterator
Iteratorfelsökningsfunktionen har utbildats för att packa upp std::move_iterator korrekt. Kan till exempel std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) nu aktivera den memcpy snabbvägen.
Åtgärder för <xkeycheck.h>-nyckelordshantering
Standardbibliotekets tillämpning i <xkeycheck.h> för makron som ersätter ett nyckelord har åtgärdats. Biblioteket genererar nu det faktiska problemnyckelordet som identifierats i stället för ett allmänt meddelande. Den stöder också C++20-nyckelord och undviker att lura IntelliSense att säga att slumpmässiga nyckelord är makron.
Allokeringstyperna är inte längre inaktuella
              std::allocator<void>, std::allocator::size_typeoch std::allocator::difference_type är inte längre inaktuella.
Rätt varning för att begränsa strängkonverteringar
Tog bort en falsk static_cast från std::string som inte krävdes av standarden, och som av misstag undertryckte C4244-varningar för typförminskning. Försök att anropa std::string::string(const wchar_t*, const wchar_t*) resulterar nu korrekt i varning C4244 om att tränga ihop en wchar_t till en char.
Olika korrigeringar för <filsystemets> korrekthet
- Åtgärdat std::filesystem::last_write_timesom misslyckades när man försökte ändra en katalogs senaste skrivtid.
- Konstruktorn std::filesystem::directory_entrylagrar nu ett misslyckat resultat, i stället för att utlösa ett undantag, när den tillhandahålls en icke-existerande målsökväg.
- 
              std::filesystem::create_directory2-parameterversionen ändrades för att anropa 1-parameterversionen, eftersom den underliggandeCreateDirectoryExWfunktionen skulle användacopy_symlinknärexisting_pvar en symlink.
- 
              std::filesystem::directory_iteratormisslyckas inte längre när en bruten symlink hittas.
- 
              std::filesystem::spaceaccepterar nu relativa sökvägar.
- 
              std::filesystem::path::lexically_relativeär inte längre förvirrad av avslutande snedstreck, rapporterat som LWG 3096.
- Arbetade med CreateSymbolicLinkWatt avvisa sökvägar med snedstreck istd::filesystem::create_symlink.
- Arbetade runt POSIX-borttagningslägefunktionen deletesom fanns i Windows 10 LTSB 1609, men kunde inte ta bort filer.
- 
              std::boyer_moore_searcher- ochstd::boyer_moore_horspool_searcher-kopieringskonstruktorer samt kopieringstilldelningsoperatorer kopierar nu faktiskt saker.
Parallella algoritmer i Windows 8 och senare
Det parallella algoritmbiblioteket använder nu den verkliga WaitOnAddress familjen på Windows 8 och senare, i stället för att alltid använda Windows 7 och tidigare falska versioner.
              std::system_category::message() blanksteg
              std::system_category::message() trimmar nu avslutande blanksteg från det returnerade meddelandet.
              std::linear_congruential_engine dividera med noll
Vissa villkor som skulle orsaka std::linear_congruential_engine att orsaka division med 0 har åtgärdats.
Korrigeringar för att packa upp iteratorn
Vissa iterator-unwrapping-maskiner exponerades först för integrering av programmerare och användare i Visual Studio 2017 15.8. Den beskrevs i C++ Team Blog artikeln STL Funktioner och korrigeringar i VS 2017 15.8. Den här maskinen packar inte längre upp iteratorer som härletts från standardbibliotekets iteratorer. En användare som till exempel härleds från std::vector<int>::iterator och försöker anpassa beteendet får nu sitt anpassade beteende när de anropar standardbiblioteksalgoritmer i stället för beteendet för en pekare.
Den osorterade containerfunktionen reserve reserverar nu faktiskt för N-element, enligt beskrivningen i LWG 2156.
Tidshantering
- Tidigare överflödade vissa tidsvärden som skickades till samtidighetsbiblioteket, till exempel - condition_variable::wait_for(seconds::max()). Efter att ha blivit åtgärdat, ändrade överflödet beteende och följde en till synes slumpmässig 29-dagars cykel (då uint32_t millisekunder som accepterats av underliggande Win32-API:er svämmade över).
- Ctime-huvudet <> deklarerar - timespecnu korrekt och- timespec_geti namnområdet- stdoch deklarerar dem även i det globala namnområdet.
Olika korrigeringar för containrar
- Många interna containerfunktioner i standardbiblioteket har skapats - privateför en förbättrad IntelliSense-upplevelse. Fler korrigeringar för att markera medlemmar som- privateförväntas vara i senare versioner av MSVC.
- Vi har åtgärdat problem med felsäkerhetskorrigering som gjorde att nodbaserade containrar, till exempel - list,- mapoch- unordered_map, skadades. Under en- propagate_on_container_copy_assignmenteller- propagate_on_container_move_assignmentomtilldelningsåtgärd frigör vi containerns sentinel-nod med den gamla allokeraren, gör POCCA/POCMA-tilldelningen över den gamla allokeraren och försöker sedan hämta sentinel-noden från den nya allokeraren. Om den här allokeringen misslyckades skadades containern. Det gick inte ens att förstöra den, eftersom ägandet av en sentinel-nod är en hård datastruktur som är invariant. Den här koden har åtgärdats för att skapa den nya sentinel-noden med hjälp av källcontainerns allokerare innan den befintliga sentinel-noden förstörs.
- Containrarna har korrigerats för att alltid kopiera/flytta/byta allokerare enligt - propagate_on_container_copy_assignment,- propagate_on_container_move_assignment, och- propagate_on_container_swap, även för allokerare som deklarerats- is_always_equal.
- Överbelastningar har lagts till för att sammanslå och extrahera medlemsfunktioner hos containrar som accepterar rvalue-containrar. Mer information finns i P0083 "Kombinering av kartor och uppsättningar" 
              std::basic_istream::read bearbetning av \r\n =>\n
              std::basic_istream::read fixades för att inte skriva in i delar av den angivna bufferten tillfälligt som en del av bearbetningen från \r\n till \n. Den här ändringen ger upp en del av den prestandafördel som erhölls i Visual Studio 2017 15.8 för läsningar större än 4K i storlek. Effektivitetsförbättringar från att undvika tre virtuella anrop per tecken finns dock fortfarande.
              std::bitset konstruktor
Konstruktorn std::bitset läser inte längre ettorna och nollorna i omvänd ordning för stora bitset.
              std::pair::operator= regression
Vi har korrigerat en regression i tilldelningsoperatorn std::pair som introducerades när LWG 2729 "Missing SFINAE on std::pair::operator=";. Den accepterar nu korrekt typer som kan konverteras till std::pair igen.
Icke-deducerade kontexter för add_const_t
Vi har åtgärdat en bugg av mindre typer av egenskaper, där add_const_t och relaterade funktioner ska vara en icke-inducerad kontext. Med andra ord add_const_t bör vara ett alias för typename add_const<T>::type, inte const T.
Överensstämmelseförbättringar i 16.1
char8_t
              P0482r6. C++20 lägger till en ny teckentyp som används för att representera UTF-8-kodenheter. 
              u8 strängliteraler i C++20 har typ const char8_t[N] i stället för const char[N], vilket var fallet tidigare. Liknande ändringar har föreslagits för C-standarden i N2231. Förslag på char8_t bakåtkompatibilitetsreparation ges i P1423r3. Microsoft C++-kompilatorn lägger till stöd för char8_t i Visual Studio 2019 version 16.1 när du anger kompileringsalternativet /Zc:char8_t . Det kan återställas till C++17-beteende via /Zc:char8_t-. EDG-kompilatorn som driver IntelliSense har ännu inte stöd för den i Visual Studio 2019 version 16.1. Du kan se missvisande IntelliSense-fel som inte påverkar den faktiska kompileringen.
Exempel
const char* s = u8"Hello"; // C++17
const char8_t* s = u8"Hello"; // C++20
              std::type_identity metafunktions- och std::identity funktionsobjekt
              P0887R1 type_identity. Det inaktuella std::identity klassmallstillägget har tagits bort och ersatts med metafunktions- och std::type_identity funktionsobjektet C++20std::identity. Båda är endast tillgängliga under /std:c++latest (/std:c++20 i Visual Studio 2019 version 16.11 och senare).
I följande exempel skapas utfasningsvarningen C4996 för std::identity (definierad i <type_traits>) i Visual Studio 2017:
#include <type_traits>
using T = std::identity<int>::type;
T x, y = std::identity<T>{}(x);
int i = 42;
long j = std::identity<long>{}(i);
I följande exempel visas hur du använder det nya std::identity (definierat i <funktion>) tillsammans med det nya std::type_identity:
#include <type_traits>
#include <functional>
using T = std::type_identity<int>::type;
T x, y = std::identity{}(x);
int i = 42;
long j = static_cast<long>(i);
Syntaxkontroller för generiska lambdas
Den nya lambda-processorn möjliggör vissa syntaktiska kontroller i överensstämmelseläge i generiska lambdas, under /std:c++latest (/std:c++20 i Visual Studio 2019 version 16.11 och senare) eller under något annat språkläge med /Zc:lambda i Visual Studio 2019 version 16.9 eller senare (tidigare tillgängligt som /experimental:newLambdaProcessor början i Visual Studio 2019 version 16.3).
Den äldre lambda-processorn kompilerar det här exemplet utan varningar, men den nya lambda-processorn genererar felet C2760:
void f() {
    auto a = [](auto arg) {
        decltype(arg)::Type t; // C2760 syntax error: unexpected token 'identifier', expected ';'
    };
}
Det här exemplet visar rätt syntax, som nu framtvingas av kompilatorn:
void f() {
    auto a = [](auto arg) {
        typename decltype(arg)::Type t;
    };
}
Argumentberoende sökning efter funktionsanrop
              P0846R0 (C++20) Ökad möjlighet att hitta funktionsmallar via argumentberoende sökning efter funktionsanropsuttryck med explicita mallargument. Kräver /std:c++latest (eller /std:c++20 i Visual Studio 2019 version 16.11 och senare).
Specifik initiering
              P0329R4 (C++20) Angiven initiering gör att vissa medlemmar kan väljas i aggregerad initiering med hjälp av syntaxen Type t { .member = expr } . Kräver /std:c++latest (eller /std:c++20 i Visual Studio 2019 version 16.11 och senare).
Rangordning av enum-konvertering till sin fasta underliggande typ
Kompilatorn rangordnar nu uppräkningskonverteringar enligt N4800 11.3.3.2 Rangordna implicita konverteringssekvenser (4.2):
- En konvertering som höjer upp en uppräkning vars underliggande typ är fast i den underliggande typen är bättre än en som höjer upp till den upphöjda underliggande typen, om de två är olika.
Den här konverteringsordningen implementerades inte korrekt före Visual Studio 2019 version 16.1. Det överensstämmande beteendet kan ändra beteendet för överbelastningsmatchning eller exponera en tvetydighet där en tidigare inte identifierades.
Den här ändringen i kompilatorns beteende gäller för alla /std lägen och är både en käll- och binär icke-bakåtkompatibel ändring.
I följande exempel visas hur kompilatorns beteende ändras i 16.1 och senare versioner:
#include <type_traits>
enum E : unsigned char { e };
int f(unsigned int)
{
    return 1;
}
int f(unsigned char)
{
    return 2;
}
struct A {};
struct B : public A {};
int f(unsigned int, const B&)
{
    return 3;
}
int f(unsigned char, const A&)
{
    return 4;
}
int main()
{
    // Calls f(unsigned char) in 16.1 and later. Called f(unsigned int) in earlier versions.
    // The conversion from 'E' to the fixed underlying type 'unsigned char' is better than the
    // conversion from 'E' to the promoted type 'unsigned int'.
    f(e);
  
    // Error C2666. This call is ambiguous, but previously called f(unsigned int, const B&). 
    f(e, B{});
}
Nya och uppdaterade standardbiblioteksfunktioner (C++20)
- 
              starts_with()ochends_with()förbasic_stringochbasic_string_view.
- 
              contains()för associativa containrar.
- 
              remove(),remove_if(), ochunique()förlistochforward_listreturnerar nusize_type.
- 
              shift_left()ochshift_right()har lagts till <i algoritmen>.
Överensstämmelseförbättringar i 16.2
              noexcept
              constexpr Funktioner
              constexpr funktioner anses noexcept inte längre automatiskt när de används i ett konstant uttryck. Den här beteendeändringen kommer från lösningen för CWG (Core Working Group) CWG 1351 och är aktiverad i /permissive-. Följande exempel kompileras i Visual Studio 2019 version 16.1 och tidigare, men producerar C2338 i Visual Studio 2019 version 16.2:
constexpr int f() { return 0; }
int main() {
    static_assert(noexcept(f()), "f should be noexcept"); // C2338 in 16.2
}
Åtgärda felet genom att lägga till noexcept uttrycket i funktionsdeklarationen:
constexpr int f() noexcept { return 0; }
int main() {
    static_assert(noexcept(f()), "f should be noexcept");
}
Binära uttryck med olika enumtyper
C++20 har föråldrat de vanliga aritmetiska konverteringarna på operander, där:
- En operand är av uppräkningstyp och 
- den andra är av en annan uppräkningstyp eller flyttalstyp. 
Mer information finns i P1120R0.
I Visual Studio 2019 version 16.2 och senare genererar följande kod en C5054-varning på nivå 4 när kompilatoralternativet /std:c++latest är aktiverat (/std:c++20 i Visual Studio 2019 version 16.11 och senare):
enum E1 { a };
enum E2 { b };
int main() {
    int i = a | b; // warning C5054: operator '|': deprecated between enumerations of different types
}
Undvik varningen genom att använda static_cast för att konvertera den andra operanden:
enum E1 { a };
enum E2 { b };
int main() {
  int i = a | static_cast<int>(b);
}
Att använda en binär åtgärd mellan en uppräkning och en flyttalstyp är nu en C5055-varning på nivå 1 när kompileringsalternativet /std:c++latest är aktiverat (/std:c++20 i Visual Studio 2019 version 16.11 och senare):
enum E1 { a };
int main() {
  double i = a * 1.1;
}
Undvik varningen genom att använda static_cast för att konvertera den andra operanden:
enum E1 { a };
int main() {
   double i = static_cast<int>(a) * 1.1;
}
Likhet och relationsjämförelser av matriser
Likhet och relationsjämförelser mellan två operander av matristyp är inaktuella i C++20 (P1120R0). Med andra ord är en jämförelseåtgärd mellan två matriser (trots likheterna mellan rangordning och omfattning) nu en varning. I Visual Studio 2019 version 16.2 och senare genererar följande kod nivå 1-varning C5056 när kompileringsalternativet /std:c++latest är aktiverat (/std:c++20 i Visual Studio 2019 version 16.11 och senare):
int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (a == b) { return 1; } // warning C5056: operator '==': deprecated for array types
}
För att undvika varningen kan du jämföra adresserna för de första elementen:
int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (&a[0] == &b[0]) { return 1; }
}
För att avgöra om innehållet i två matriser är lika, använd funktionen std::equal.
std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
Effekten av att definiera rymdskeppsoperatorn på == och !=
En definition av enbart rymdskeppsoperatorn (<=>) skriver inte längre om uttryck som involverar == eller != om inte rymdskeppsoperatorn har markerats som = default (P1185R2). Följande exempel kompileras i Visual Studio 2019 RTW och version 16.1, men producerar C2678 i Visual Studio 2019 version 16.2:
#include <compare>
struct S {
  int a;
  auto operator<=>(const S& rhs) const {
    return a <=> rhs.a;
  }
};
bool eq(const S& lhs, const S& rhs) {
  return lhs == rhs; // error C2676
}
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs; // error C2676
}
För att undvika felet, definiera operator== eller deklarera det som förvalt.
#include <compare>
struct S {
  int a;
  auto operator<=>(const S& rhs) const {
    return a <=> rhs.a;
  }
  bool operator==(const S&) const = default;
};
bool eq(const S& lhs, const S& rhs) {
  return lhs == rhs;
}
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;
}
Standardbiblioteksförbättringar
- 
              <charconv>to_chars()med fast/vetenskaplig precision. (Allmän precision är för närvarande planerad till 16,4.)
- 
              P0020R6: atomic<float>,atomic<double>,atomic<long double>
- P0463R1: Endian
- 
              P0482R6: Biblioteksstöd för char8_t
- 
              P0600R1: [[nodiscard]]För STL, del 1
- 
              P0653R2: to_address()
- P0754R2: <version>
- 
              P0771R1: noexceptFörstd::functionflyttkonstruktorn
Const-jämförelse för associativa containrar
Kod för sökning och infogning i set, map, multisetoch multimap har sammanfogats för minskad kodstorlek. Infogningsoperationer anropar nu mindre än-jämförelsen på en const jämförelsefunktor på samma sätt som sökoperationer har gjort tidigare. Följande kod kompileras i Visual Studio 2019 version 16.1 och tidigare, men genererar C3848 i Visual Studio 2019 version 16.2:
#include <iostream>
#include <map>
using namespace std;
struct K
{
   int a;
   string b = "label";
};
struct Comparer  {
   bool operator() (K a, K b) {
      return a.a < b.a;
   }
};
map<K, double, Comparer> m;
K const s1{1};
K const s2{2};
K const s3{3};
int main() {
   m.emplace(s1, 1.08);
   m.emplace(s2, 3.14);
   m.emplace(s3, 5.21);
}
Undvik felet genom att göra jämförelseoperatorn const:
struct Comparer  {
   bool operator() (K a, K b) const {
      return a.a < b.a;
   }
};
Anpassningsförbättringar i Visual Studio 2019 version 16.3
Strömextraheringsoperatorer för char* borttagna
Strömextraheringsoperatorer för pekare till tecken har tagits bort och ersatts av extraheringsoperatorer för matris med tecken (per P0487R1). WG21 anser att de borttagna överladdningarna är osäkra. I /std:c++20- eller /std:c++latest-läge skapar följande exempel nu C2679:
// stream_extraction.cpp
// compile by using: cl /std:c++latest stream_extraction.cpp
#include <iostream>
#include <iomanip>
int main() {
    char x[42];
    char* p = x;
    std::cin >> std::setw(42);
    std::cin >> p;  // C2679: binary '>>': no operator found which takes a right-hand operand of type 'char *' (or there is no acceptable conversion)
}
Undvik felet genom att använda extraheringsoperatorn med en char[] variabel:
#include <iostream>
#include <iomanip>
int main() {
    char x[42];
    std::cin >> std::setw(42);
    std::cin >> x;  // OK
}
Nya nyckelord requires och concept
Nya nyckelord requires och concept har lagts till i Microsoft C++-kompilatorn. Om du försöker använda någon av dem som identifierare i /std:c++20-läge eller /std:c++latest-läge, genererar kompilatorn C2059 för att indikera ett syntaxfel.
Konstruktorer som typnamn tillåts inte
Kompilatorn betraktar inte längre konstruktornamn som inmatade klassnamn i det här fallet: när de visas i ett kvalificerat namn efter ett alias för en klassmallsspecialisering. Tidigare kunde konstruktorer användas som ett typnamn för att deklarera andra entiteter. I följande exempel skapas nu C3646:
#include <chrono>
class Foo {
   std::chrono::milliseconds::duration TotalDuration{}; // C3646: 'TotalDuration': unknown override specifier
};
För att undvika felet deklarerar TotalDuration du så här:
#include <chrono>
class Foo {
  std::chrono::milliseconds TotalDuration {};
};
Striktare kontroll av extern "C" funktioner
Om en extern "C" funktion deklarerades i olika namnområden kontrollerade inte tidigare versioner av Microsoft C++-kompilatorn om deklarationerna var kompatibla. I Visual Studio 2019 version 16.3 och senare söker kompilatorn efter kompatibilitet. I /permissive- läge genererar följande kod felen C2371 och C2733:
using BOOL = int;
namespace N
{
   extern "C" void f(int, int, int, bool);
}
void g()
{
   N::f(0, 1, 2, false);
}
extern "C" void f(int, int, int, BOOL){}
    // C2116: 'N::f': function parameter lists do not match between declarations
    // C2733: 'f': you cannot overload a function with 'extern "C"' linkage
Undvik felen i föregående exempel genom att använda bool i stället för BOOL konsekvent i båda deklarationerna av f.
Standardbiblioteksförbättringar
Icke-standardrubrikerna <stdexcpt.h> och <typeinfo.h> har tagits bort. Kod som innehåller dem bör i stället innehålla standardhuvudena <undantag> respektive <typeinfo>.
Anpassningsförbättringar i Visual Studio 2019 version 16.4
Bättre tillämpning av tvåfasnamnsökning för kvalificerade ID:er i /permissive-
Tvåfasnamnsökning kräver att icke-beroende namn som används i mallkroppar måste vara synliga för mallen vid definitionstillfället. Tidigare kan sådana namn ha hittats när mallen instansieras. Den här ändringen gör det enklare att skriva portabel och överensstämmande kod i MSVC under /permissive- flaggan.
I Visual Studio 2019 version 16.4 med flagguppsättningen /permissive- genererar följande exempel ett fel, eftersom N::f det inte visas när mallen f<T> definieras:
template <class T>
int f() {
    return N::f() + T{}; // error C2039: 'f': is not a member of 'N'
}
namespace N {
    int f() { return 42; }
}
Normalt kan det här felet åtgärdas genom att inkludera saknade rubriker eller framåtklarerande funktioner eller variabler, som du ser i följande exempel:
namespace N {
    int f();
}
template <class T>
int f() {
    return N::f() + T{};
}
namespace N {
    int f() { return 42; }
}
Implicit konvertering av integralkonstantuttryck till nullpekare
MSVC-kompilatorn implementerar nu CWG Issue 903 i efterlevnadsläge (/permissive-). Den här regeln tillåter inte implicit konvertering av heltalskonstantuttryck (förutom heltalsliteralen "0") till nullpekarkonstanter. I följande exempel skapas C2440 i överensstämmelseläge:
int* f(bool* p) {
    p = false; // error C2440: '=': cannot convert from 'bool' to 'bool *'
    p = 0; // OK
    return false; // error C2440: 'return': cannot convert from 'bool' to 'int *'
}
Åtgärda felet genom att använda nullptr i stället för false. Ett literal 0 tillåts fortfarande.
int* f(bool* p) {
    p = nullptr; // OK
    p = 0; // OK
    return nullptr; // OK
}
Standardregler för typer av heltalsliteraler
I överensstämmelseläge (aktiverat av /permissive-) använder MSVC standardreglerna för typer av heltal. Decimalliteraler som är för stora för att få plats i en signed int har tidigare angetts som unsigned int. Nu får sådana literaler den näst största signed heltalstypen, long long. Dessutom ges literaler med suffixet "ll" som är för stora för att få plats i en signed typ unsigned long long.
Den här ändringen kan leda till att olika varningsmeddelanden genereras och skillnader i beteende vid aritmetiska operationer med literaler.
I följande exempel visas det nya beteendet i Visual Studio 2019 version 16.4. Variabeln i är nu av typen unsigned int, så varningen aktiveras. Variabelns högordningsbitar är inställda på j 0.
void f(int r) {
    int i = 2964557531; // warning C4309: truncation of constant value
    long long j = 0x8000000000000000ll >> r; // literal is now unsigned, shift will fill high-order bits with 0
}
I följande exempel visas hur du behåller det gamla beteendet och undviker varningarna och beteendeförändringen vid körning:
void f(int r) {
int i = 2964557531u; // OK
long long j = (long long)0x8000000000000000ll >> r; // shift will keep high-order bits
}
Funktionsparametrar som skuggar mallparametrar
MSVC-kompilatorn genererar nu ett fel när en funktionsparameter skuggar en mallparameter:
template<typename T>
void f(T* buffer, int size, int& size_read);
template<typename T, int Size>
void f(T(&buffer)[Size], int& Size) // error C7576: declaration of 'Size' shadows a template parameter
{
    return f(buffer, Size, Size);
}
Åtgärda felet genom att ändra namnet på någon av parametrarna:
template<typename T>
void f(T* buffer, int size, int& size_read);
template<typename T, int Size>
void f(T (&buffer)[Size], int& size_read)
{
    return f(buffer, Size, size_read);
}
Specialiseringar av typegenskaper som tillhandahålls av användaren
I enlighet med delmeningen meta.rqmts för Standarden skapar MSVC-kompilatorn nu ett felmeddelande när den hittar en användardefinierad specialanpassning av en av de angivna type_traits mallarna i std namnområdet. Om inget annat anges resulterar sådana specialiseringar i odefinierat beteende. Följande exempel har odefinierat beteende eftersom det bryter mot regeln och static_assert misslyckas med felet C2338.
#include <type_traits>
struct S;
template<>
struct std::is_fundamental<S> : std::true_type {};
static_assert(std::is_fundamental<S>::value, "fail");
För att undvika felet, definiera en struct som ärver från den önskade type_trait, och specialisera den.
#include <type_traits>
struct S;
template<typename T>
struct my_is_fundamental : std::is_fundamental<T> {};
template<>
struct my_is_fundamental<S> : std::true_type { };
static_assert(my_is_fundamental<S>::value, "fail");
Ändringar i jämförelseoperatorer som tillhandahålls av kompilatorn
MSVC-kompilatorn implementerar nu följande ändringar av jämförelseoperatorer per P1630R1 när /std:c++20 alternativet eller /std:c++latest är aktiverat:
Kompilatorn skriver inte längre om uttryck som använder operator== om de innehåller en returtyp som inte är en bool. Följande kod genererar nu felet C2088:
struct U {
    operator bool() const;
};
struct S {
    U operator==(const S&) const;
};
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;  // C2088: '!=': illegal for struct
}
För att undvika felet måste du uttryckligen definiera den nödvändiga operatorn:
struct U {
    operator bool() const;
};
struct S {
    U operator==(const S&) const;
    U operator!=(const S&) const;
};
bool neq(const S& lhs, const S& rhs) {
    return lhs != rhs;
}
Kompilatorn definierar inte längre en standardmässig jämförelseoperator om den är medlem i en unionsliknande klass. Följande exempel genererar nu felet C2120:
#include <compare>
union S {
    int a;
    char b;
    auto operator<=>(const S&) const = default;
};
bool lt(const S& lhs, const S& rhs) {
    return lhs < rhs;
}
För att undvika felet definierar du en brödtext för operatorn:
#include <compare>
union S {
    int a;
    char b;
    auto operator<=>(const S&) const { ... }
};
bool lt(const S& lhs, const S& rhs) {
    return lhs < rhs;
}
Kompilatorn definierar inte längre en standardmässig jämförelseoperator om klassen innehåller en referensmedlem. Följande kod genererar nu felet C2120:
#include <compare>
struct U {
    int& a;
    auto operator<=>(const U&) const = default;
};
bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}
För att undvika felet definierar du en brödtext för operatorn:
#include <compare>
struct U {
    int& a;
    auto operator<=>(const U&) const { ... };
};
bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}
Anpassningsförbättringar i Visual Studio 2019 version 16.5
Explicit specialiseringsdeklaration utan en initierare är inte en definition
Under /permissive-tillämpar MSVC nu en standardregel om att explicita specialiseringsdeklarationer utan initialiserare inte är definitioner. Tidigare skulle deklarationen betraktas som en definition med en standardinitialiserare. Effekten kan observeras vid länktid, eftersom ett program som är beroende av det här beteendet nu kan ha olösta symboler. Det här exemplet resulterar nu i ett fel:
template <typename> struct S {
    static int a;
};
// In permissive-, this declaration isn't a definition, and the program won't link.
template <> int S<char>::a;
int main() {
    return S<char>::a;
}
error LNK2019: unresolved external symbol "public: static int S<char>::a" (?a@?$S@D@@2HA) referenced in function _main at link time.
Lös problemet genom att lägga till en initiator:
template <typename> struct S {
    static int a;
};
// Add an initializer for the declaration to be a definition.
template <> int S<char>::a{};
int main() {
    return S<char>::a;
}
Förprocessorutdata bevarar radbrytningar
Den experimentella förprocessorn bevarar nu nya linjer och blanksteg när du använder /P eller /E med /experimental:preprocessor.
Med tanke på den här exempelkällan
#define m()
line m(
) line
Föregående utdata av /E var:
line line
#line 2
De nya utdata för /E är nu:
line
 line
              import och module nyckelord är kontextberoende
Enligt P1857R1 har import och module förprocessordirektiven nya begränsningar i syntaxen. Det här exemplet kompileras inte längre:
import // Invalid
m;     // error C2146: syntax error: missing ';' before identifier 'm'
Lös problemet genom att behålla importen på samma rad:
import m; // OK
Borttagning av std::weak_equality och std::strong_equality
Sammanfogningen av P1959R0 kräver att kompilatorn tar bort beteende och referenser till typerna std::weak_equality och std::strong_equality .
Koden i det här exemplet kompileras inte längre:
#include <compare>
struct S {
    std::strong_equality operator<=>(const S&) const = default;
};
void f() {
    nullptr<=>nullptr;
    &f <=> &f;
    &S::operator<=> <=> &S::operator<=>;
}
Exemplet leder nu till följande fel:
error C2039: 'strong_equality': is not a member of 'std'
error C2143: syntax error: missing ';' before '<=>'
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
error C7546: binary operator '<=>': unsupported operand types 'nullptr' and 'nullptr'
error C7546: binary operator '<=>': unsupported operand types 'void (__cdecl *)(void)' and 'void (__cdecl *)(void)'
error C7546: binary operator '<=>': unsupported operand types 'int (__thiscall S::* )(const S &) const' and 'int (__thiscall S::* )(const S &) const'
Lös problemet genom att uppdatera för att föredra de inbyggda relationsoperatorerna och ersätta de borttagna typerna:
#include <compare>
struct S {
    std::strong_ordering operator<=>(const S&) const = default; // prefer 'std::strong_ordering'
};
void f() {
    nullptr != nullptr; // use pre-existing builtin operator != or ==.
    &f != &f;
    &S::operator<=> != &S::operator<=>;
}
TLS Guard-ändringar
Tidigare initierades inte trådlokala variabler i DLL:er korrekt. Förutom i tråden som läste in DLL-filen initierades de inte innan de först användes på trådar som fanns innan DLL-filen lästes in. Den här defekten har nu korrigerats. Trådlokala variabler i en sådan DLL initieras omedelbart före den första användningen av sådana trådar.
Det här nya beteendet för testning för initiering av användning av trådlokala variabler kan inaktiveras med hjälp av kompilatoralternativet /Zc:tlsGuards- . Eller genom att lägga till [[msvc::no_tls_guard]] attributet i vissa lokala trådvariabler.
Bättre diagnos av anrop till borttagna funktioner
Vår kompilator var mer tillåtande när det gäller anrop till borttagna funktioner tidigare. Om anropen till exempel skedde i kontexten för en malltext skulle vi inte diagnostisera anropet. Och om det fanns flera instanser av anrop till borttagna funktioner skulle vi dessutom bara utfärda ett diagnostiskt meddelande. Nu utfärdar vi en diagnostik för var och en av dem.
En konsekvens av det nya beteendet kan orsaka en liten ändring som bryter: Kod som anropade en borttagen funktion skulle inte diagnostiseras om den aldrig behövdes för kodgenerering. Nu diagnostiserar vi det i förväg.
Det här exemplet visar kod som nu genererar ett fel:
struct S {
  S() = delete;
  S(int) { }
};
struct U {
  U() = delete;
  U(int i): s{ i } { }
  S s{};
};
U u{ 0 };
error C2280: 'S::S(void)': attempting to reference a deleted function
note: see declaration of 'S::S'
note: 'S::S(void)': function was explicitly deleted
Lös problemet genom att ta bort anrop till borttagna funktioner:
struct S {
  S() = delete;
  S(int) { }
};
struct U {
  U() = delete;
  U(int i): s{ i } { }
  S s;  // Do not call the deleted ctor of 'S'.
};
U u{ 0 };
Anpassningsförbättringar i Visual Studio 2019 version 16.6
Standardbiblioteksströmmar avvisar infogningar av felkodade teckentyper
Traditionellt resulterar det i dess integralvärde när en wchar_t sätts in i en std::ostream, och char16_t eller char32_t sätts in i en std::ostream eller std::wostream. Om du infogar pekare till dessa teckentyper matas pekarvärdet ut. Programmerare tycker inte att något av fallen är intuitivt. De förväntar sig ofta att standardbiblioteket omkodar tecknet eller den null-avslutade teckensträngen i stället och matar ut resultatet.
C++20-förslaget P1423R3 lägger till borttagna ströminfogningsoperatoröverbelastningar för dessa kombinationer av dataström och tecken- eller teckenpekartyper. Under /std:c++20 eller /std:c++latest gör överlagren dessa infogningar felaktigt utformade, i stället för att bete sig på ett sätt som sannolikt är oavsiktligt. Kompilatorn genererar felet C2280 när ett felvillkor upptäcks. "Du kan definiera 'escape hatch'-makrot _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 till 1 för att återställa det gamla beteendet." (Förslaget tar också bort ströminsättningsoperatorer för char8_t. Vårt standardbibliotek implementerade liknande överbelastningar när vi lade till stöd för char8_t, så det 'felaktiga' beteendet har aldrig varit tillgängligt för char8_t.)
Det här exemplet visar beteendet med den här ändringen:
#include <iostream>
int main() {
    const wchar_t cw = L'x', *pw = L"meow";
    const char16_t c16 = u'x', *p16 = u"meow";
    const char32_t c32 = U'x', *p32 = U"meow";
    std::cout << cw << ' ' << pw << '\n';
    std::cout << c16 << ' ' << p16 << '\n';
    std::cout << c32 << ' ' << p32 << '\n';
    std::wcout << c16 << ' ' << p16 << '\n';
    std::wcout << c32 << ' ' << p32 << '\n';
}
Koden genererar nu följande diagnostikmeddelanden:
error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,wchar_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char16_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<char,std::char_traits<char>> &std::<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char32_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::<<<std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,char16_t)': attempting to reference a deleted function
error C2280: 'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::<<<std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,char32_t)': attempting to reference a deleted function
Du kan uppnå effekten av det gamla beteendet i alla språklägen genom att konvertera teckentyper till unsigned int, eller pekar-till-tecken-typer till const void*:
#include <iostream>
int main() {
    const wchar_t cw = L'x', *pw = L"meow";
    const char16_t c16 = u'x', *p16 = u"meow";
    const char32_t c32 = U'x', *p32 = U"meow";
    std::cout << (unsigned)cw << ' ' << (const void*)pw << '\n'; // Outputs "120 0052B1C0"
    std::cout << (unsigned)c16 << ' ' << (const void*)p16 << '\n'; // Outputs "120 0052B1CC"
    std::cout << (unsigned)c32 << ' ' << (const void*)p32 << '\n'; // Outputs "120 0052B1D8"
    std::wcout << (unsigned)c16 << ' ' << (const void*)p16 << '\n'; // Outputs "120 0052B1CC"
    std::wcout << (unsigned)c32 << ' ' << (const void*)p32 << '\n'; // Outputs "120 0052B1D8"
}
Ändrad returtyp std::pow() för std::complex
Tidigare var MSVC-implementeringen av kampanjreglerna för returtypen av funktionsmallen std::pow() felaktig. Tidigare returnerade pow(complex<float>, int)till exempel complex<float> . Nu returneras korrekt complex<double>. Korrigeringen har implementerats villkorslöst för alla standardlägen i Visual Studio 2019 version 16.6.
Den här ändringen kan orsaka kompilatorfel. Tidigare kunde du till exempel multiplicera pow(complex<float>, int) med en float. Eftersom complex<T> operator* förväntar sig argument av samma typ genererar följande exempel nu kompilatorfel C2676:
// pow_error.cpp
// compile by using: cl /EHsc /nologo /W4 pow_error.cpp
#include <complex>
int main() {
    std::complex<float> cf(2.0f, 0.0f);
    (void) (std::pow(cf, -1) * 3.0f);
}
pow_error.cpp(7): error C2676: binary '*': 'std::complex<double>' does not define this operator or a conversion to a type acceptable to the predefined operator
Det finns många möjliga korrigeringar:
- Ändra typen av - floatmultiplicand till- double. Det här argumentet kan konverteras direkt till en- complex<double>för att matcha den typ som returneras av- pow.
- Begränsa resultatet av - powtill- complex<float>genom att säga- complex<float>{pow(ARG, ARG)}. Sedan kan du fortsätta att multiplicera med ett- floatvärde.
- Skicka - floati stället- intför till- pow. Den här åtgärden kan vara långsammare.
- I vissa fall kan du undvika - powhelt. Kan till exempel- pow(cf, -1)ersättas med division.
              switch varningar för C
I Visual Studio 2019 version 16.6 och senare implementerar kompilatorn några befintliga C++-varningar för kod som kompilerats som C. Följande varningar är nu aktiverade på olika nivåer: C4060, C4061, C4062, C4063, C4064, C4065, C4808 och C4809. Varningar C4065 och C4060 är inaktiverade som standard i C.
Varningar utlöses av saknade case-instruktioner, odefinierade enum och felaktiga bool switch-instruktioner (det vill säga sådana som innehåller för många fall). Till exempel:
#include <stdbool.h>
int main() {
    bool b = true;
    switch (b) {
        case true: break;
        case false: break;
        default: break; // C4809: switch statement has redundant 'default' label;
                        // all possible 'case' labels are given
    }
}
Åtgärda den här koden genom att ta bort det redundanta default ärendet:
#include <stdbool.h>
int main() {
    bool b = true;
    switch (b) {
        case true: break;
        case false: break;
    }
}
Namnlösa klasser i typedef deklarationer
I Visual Studio 2019 version 16.6 och senare har beteendet typedef för deklarationer begränsats till att överensstämma med P1766R1. Med den här uppdateringen kan icke namngivna klasser i en typedef deklaration inte ha några andra medlemmar än:
- icke-statiska datamedlemmar utan standardmedleminitierare,
- medlemsklasser, eller
- medlemsuppräkningar.
Samma begränsningar tillämpas rekursivt på varje kapslad klass. Begränsningen är avsedd att säkerställa enkelheten i structs som har typedef namn för länkningsändamål. De måste vara tillräckligt enkla för att inga länkningsberäkningar krävs innan kompilatorn kommer till typedef namnet på länkningen.
Den här ändringen påverkar kompilatorns alla standardlägen. I standardlägen (/std:c++14) och /std:c++17 -lägen genererar kompilatorn varning C5208 för icke-överensstämmande kod. Om /permissive- anges genererar kompilatorn varning C5208 som ett fel under /std:c++14 och genererar felet C7626 under /std:c++17. Kompilatorn genererar felet C7626 för icke-överensstämmande kod när /std:c++20 eller /std:c++latest anges.
Följande exempel visar de konstruktioner som inte längre tillåts i namnlösa structs. Beroende på det angivna standardläget genereras C5208- eller C7626-fel eller -varningar:
struct B { };
typedef struct : B { // inheriting from 'B'; ill-formed
    void f(); // ill-formed
    static int i; // ill-formed
    struct U {
        void f(); // nested class has non-data member; ill-formed
    };
    int j = 10; // default member initializer; ill-formed
} S;
Koden ovan kan åtgärdas genom att ge den namnlösa klassen ett namn:
struct B { };
typedef struct S_ : B {
    void f();
    static int i;
    struct U {
        void f();
    };
    int j = 10;
} S;
Standardargumentimport i C++/CLI
Ett ökande antal API:er har standardargument i .NET Core. Därför stöder vi nu standardargumentimport i C++/CLI. Den här ändringen kan bryta befintlig kod där flera överlagringar deklareras, som i det här exemplet:
public class R {
    public void Func(string s) {}   // overload 1
    public void Func(string s, string s2 = "") {} // overload 2;
}
När den här klassen importeras till C++/CLI orsakar ett anrop till en av överlagringarna ett fel:
    (gcnew R)->Func("abc"); // error C2668: 'R::Func' ambiguous call to overloaded function
Kompilatorn genererar felet C2668 eftersom båda överlagringarna matchar den här argumentlistan. I den andra överlagringen fylls det andra argumentet i med standardargumentet. Du kan undvika det här problemet genom att ta bort den redundanta överlagringen (1). Du kan också använda den fullständiga argumentlistan och uttryckligen ange standardargumenten.
Anpassningsförbättringar i Visual Studio 2019 version 16.7
är trivialt kopierbar definition
C++20 ändrade definitionen av är trivialt kopierbar. När en klass har en icke-statisk datamedlem med volatile kvalificerad typ innebär det inte längre att någon kompilatorgenererad kopierings- eller flyttkonstruktor, eller tilldelningsoperator för kopiering eller flytt, är icke-trivial. C++ Standard-kommittén tillämpade denna ändring retroaktivt som en defektrapport. I MSVC ändras inte kompilatorns beteende i olika språklägen, till exempel /std:c++14 eller /std:c++latest.
Här är ett exempel på det nya beteendet:
#include <type_traits>
struct S
{
    volatile int m;
};
static_assert(std::is_trivially_copyable_v<S>, "Meow!");
Den här koden kompileras inte i versioner av MSVC före Visual Studio 2019 version 16.7. Det finns en off-by-default-kompilatorvarning som du kan använda för att identifiera den här ändringen. Om du kompilerar koden ovan med hjälp cl /W4 /w45220av visas följande varning:
warning C5220: `'S::m': a non-static data member with a volatile qualified type no longer implies that compiler generated copy/move constructors and copy/move assignment operators are non trivial`
Konverteringar mellan pekare till medlemmar och strängliterala konverteringar till bool begränsas
C++ Standard-utskottet antog nyligen defektrapport P1957R2, som betraktar T* det bool som en snävare omvandling. MSVC har åtgärdat en bugg i sin implementation som tidigare skulle identifiera T* till bool som en typförminskning, men inte diagnostiserade konverteringen av en strängliteral till bool eller en pekare-till-medlem till bool.
Följande program är dåligt utformat i Visual Studio 2019 version 16.7:
struct X { bool b; };
void f(X);
int main() {
    f(X { "whoops?" }); // error: conversion from 'const char [8]' to 'bool' requires a narrowing conversion
    int (X::* p) = nullptr;
    f(X { p }); // error: conversion from 'int X::*' to 'bool' requires a narrowing conversion
}
Om du vill korrigera den här koden lägger du antingen till explicita jämförelser i nullptreller undviker kontexter där begränsade konverteringar är dåligt utformade:
struct X { bool b; };
void f(X);
int main() {
    f(X { "whoops?" != nullptr }); // Absurd, but OK
    int (X::* p) = nullptr;
    f(X { p != nullptr }); // OK
}
              nullptr_t är endast konvertibel till bool som en direktinitiering
I C++11 kan nullptr endast konverteras till bool genom en direktkonvertering; till exempel när du initierar en bool med hjälp av en initialiseringslista med klammerparenteser. Den här begränsningen tillämpades aldrig av MSVC. MSVC implementerar nu regeln under /permissive-. Implicita konverteringar diagnostiseras nu som dåligt utformade. En kontextbaserad konvertering till bool tillåts fortfarande eftersom direktinitieringen bool b(nullptr) är giltig.
I de flesta fall kan felet åtgärdas genom att nullptr ersätta med false, som du ser i det här exemplet:
struct S { bool b; };
void g(bool);
bool h() { return nullptr; } // error, should be 'return false;'
int main() {
    bool b1 = nullptr; // error: cannot convert from 'nullptr' to 'bool'
    S s { nullptr }; // error: cannot convert from 'nullptr' to 'bool'
    g(nullptr); // error: cannot convert argument 1 from 'nullptr' to 'bool'
    bool b2 { nullptr }; // OK: Direct-initialization
    if (!nullptr) {} // OK: Contextual conversion to bool
}
Överensstämmande initieringsbeteende för fältinitialiseringar med saknade initierare
Tidigare hade MSVC ett inkompatibilitetsbeteende för matrisinitieringar som saknade initialiserare. MSVC anropade alltid standardkonstruktorn för varje matriselement som inte hade någon initialiserare. Standardbeteendet är att initiera varje element med en tom braced-initializer-list ({}). Initieringskontexten för en tom braced-initializer-list är copy-initialization, som inte tillåter anrop till explicita konstruktorer. Det kan också finnas skillnader vid körningstid eftersom användning av {} för att initialisera kan, istället för standardkonstruktorn, anropa en konstruktor som tar en std::initializer_list. Det överensstämmande beteendet är aktiverat under /permissive-.
Här är ett exempel på det ändrade beteendet:
struct B {
    explicit B() {}
};
void f() {
    B b1[1]{}; // Error in /permissive-, because aggregate init calls explicit ctor
    B b2[1]; // OK: calls default ctor for each array element
}
Initieringen av klassmedlemmar med överlagrade namn sker i korrekt ordning
Vi har identifierat en bugg i klassdatamedlemmarnas interna representation när ett typnamn också överbelastas som en datamedlems namn. Den här buggen orsakade inkonsekvenser i aggregerad initiering och medlemsinitieringsordning. Den genererade initieringskoden är nu korrekt. Den här ändringen kan dock leda till fel eller varningar i källan som oavsiktligt förlitade sig på felordnade medlemmar, som i det här exemplet:
// Compiling with /w15038 now gives:
// warning C5038: data member 'Outer::Inner' will be initialized after data member 'Outer::v'
struct Outer {
    Outer(int i, int j) : Inner{ i }, v{ j } {}
    struct Inner { int x; };
    int v;
    Inner Inner; // 'Inner' is both a type name and data member name in the same scope
};
I tidigare versioner skulle konstruktorn felaktigt initiera datamedlemmen Inner före datamedlemmen v. (C++-standarden kräver en initieringsordning som är samma som medlemmarnas deklarationsordning). Nu när den genererade koden följer standarden är initieringslistan för medlemmar ur ordning. Kompilatorn genererar en varning för det här exemplet. Du åtgärdar det genom att ändra ordning på medlemsinitieringslistan så att den återspeglar deklarationsordningen.
Överbelastningslösning som omfattar integrala överbelastningar och long argument
C++-standarden kräver rangordning av en long till int konvertering som en standardkonvertering. Tidigare MSVC-kompilatorer rankar det felaktigt som en integrerad befordran, som rankas högre för överbelastningsmatchning. Den här rangordningen kan orsaka att överbelastningsupplösningen görs framgångsrikt när den borde betraktas som tvetydig.
Kompilatorn anser nu att rangordningen är korrekt i /permissive- läge. Ogiltig kod diagnostiseras korrekt, som i det här exemplet:
void f(long long);
void f(int);
int main() {
    long x {};
    f(x); // error: 'f': ambiguous call to overloaded function
    f(static_cast<int>(x)); // OK
}
Du kan åtgärda problemet på flera sätt:
- På anropsplatsen ändrar du typen av det skickade argumentet till - int. Du kan antingen ändra variabeltypen eller casta den.
- Om det finns många anrop kan du lägga till ytterligare en överbelastning som tar ett - longargument. I den här funktionen gjuter och vidarebefordrar du argumentet till överlagringen- int.
Användning av odefinierad variabel med intern länkning
Versioner av MSVC före Visual Studio 2019 version 16.7 accepterade användning av en variabel som deklarerades extern som hade intern länkning och inte definierades. Sådana variabler kan inte definieras i någon annan översättningsenhet och kan inte bilda ett giltigt program. Kompilatorn diagnostiserar nu det här fallet vid kompileringstillfället. Felet liknar felet för odefinierade statiska funktioner.
namespace {
    extern int x; // Not a definition, but has internal linkage because of the anonymous namespace
}
int main()
{
    return x; // Use of 'x' that no other translation unit can possibly define.
}
Det här programmet kompilerades och länkades tidigare felaktigt, men genererar nu felet C7631.
error C7631: 'anonymous-namespace::x': variable with internal linkage declared but not defined
Sådana variabler måste definieras i samma översättningsenhet som de används i. Du kan till exempel ange en explicit initierare eller en separat definition.
Typkompletthet och konverteringar från härledd till baspekare
I C++-standarder före C++20 krävde en konvertering från en härledd klass till en basklass inte att den härledda klassen var en fullständig klasstyp. C++-standardkommittén har godkänt en retroaktiv ändring av defektrapporten som gäller för alla versioner av C++-språket. Den här ändringen justerar konverteringsprocessen med typegenskaper, till exempel std::is_base_of, som kräver att den härledda klassen är en fullständig klasstyp.
Här är ett exempel:
template<typename A, typename B>
struct check_derived_from
{
    static A a;
    static constexpr B* p = &a;
};
struct W { };
struct X { };
struct Y { };
// With this change this code will fail as Z1 is not a complete class type
struct Z1 : X, check_derived_from<Z1, X>
{
};
// This code failed before and it will still fail after this change
struct Z2 : check_derived_from<Z2, Y>, Y
{
};
// With this change this code will fail as Z3 is not a complete class type
struct Z3 : W
{
    check_derived_from<Z3, W> cdf;
};
Den här beteendeändringen gäller för alla C++-språklägen för MSVC, inte bara /std:c++20 eller /std:c++latest.
Smalningskonverteringar identifieras mer konsekvent
MSVC genererar en varning för att begränsa konverteringar i en initialiserare med klammerparenteser. Tidigare diagnostiserade inte kompilatorn förminskande konverteringar från större enum underliggande typer till mindre integraltyper. (Kompilatorn ansåg felaktigt att de var en integrerad befordran i stället för en konvertering). Om den begränsade konverteringen är avsiktlig kan du undvika varningen med hjälp av ett static_cast på initieringsargumentet. Eller välj en större integral datatyp för målet.
Här är ett exempel på hur du använder en explicit static_cast för att åtgärda varningen:
enum E : long long { e1 };
struct S { int i; };
void f(E e) {
    S s = { e }; // warning: conversion from 'E' to 'int' requires a narrowing conversion
    S s1 = { static_cast<int>(e) }; // Suppress warning with explicit conversion
}
Anpassningsförbättringar i Visual Studio 2019 version 16.8
Tillägget "Class rvalue used as lvalue" (Klassrvalue används som lvalue)
MSVC har en extension som tillåter användning av ett klass-"rvalue" som ett "lvalue". Tillägget förlänger inte livslängden för klassens rvalue och kan leda till odefinierat beteende vid körning. Nu framtvingar vi standardregeln och tillåter inte det här tillägget under /permissive-.
Om du inte kan använda /permissive- ännu kan du använda /we4238 för att uttryckligen neka tillägget. Här är ett exempel:
// Compiling with /permissive- now gives:
// error C2102: '&' requires l-value
struct S {};
S f();
void g()
{
    auto p1 = &(f()); // The temporary returned by 'f' is destructed after this statement. So 'p1' points to an invalid object.
    const auto &r = f(); // This extends the lifetime of the temporary returned by 'f'
    auto p2 = &r; // 'p2' points to a valid object
}
Tillägget "Explicit specialisering i omfång som inte är namnområde"
MSVC hade ett tillägg som tillät explicit specialisering i icke-namnområdesomfång. Det är nu en del av standarden, efter upplösningen av CWG 727. Det finns dock beteendeskillnader. Vi har justerat kompilatorns beteende så att det överensstämmer med standarden.
// Compiling with 'cl a.cpp b.cpp /permissive-' now gives:
//   error LNK2005: "public: void __thiscall S::f<int>(int)" (??$f@H@S@@QAEXH@Z) already defined in a.obj
// To fix the linker error,
// 1. Mark the explicit specialization with 'inline' explicitly. Or,
// 2. Move its definition to a source file.
// common.h
struct S {
    template<typename T> void f(T);
    template<> void f(int);
};
// This explicit specialization is implicitly inline in the default mode.
template<> void S::f(int) {}
// a.cpp
#include "common.h"
int main() {}
// b.cpp
#include "common.h"
Söka efter abstrakta klasstyper
C++20 Standard ändrade den processkompilator som används för att identifiera användningen av en abstrakt klasstyp som en funktionsparameter. Mer specifikt är det inte längre ett SFINAE-fel. Om kompilatorn tidigare upptäckte att en specialisering av en funktionsmall skulle ha en abstrakt klasstypsinstans som en funktionsparameter, skulle specialiseringen betraktas som dåligt utformad. Det skulle inte läggas till i uppsättningen av kandidatfunktioner som är gångbara. I C++20 sker inte kontrollen av en parameter av abstrakt klasstyp förrän funktionen anropas. Effekten är att kod som används för kompilering inte orsakar något fel. Här är ett exempel:
class Node {
public:
    int index() const;
};
class String : public Node {
public:
    virtual int size() const = 0;
};
class Identifier : public Node {
public:
    const String& string() const;
};
template<typename T>
int compare(T x, T y)
{
    return x < y ? -1 : (x > y ? 1 : 0);
}
int compare(const Node& x, const Node& y)
{
    return compare(x.index(), y.index());
}
int f(const Identifier& x, const String& y)
{
    return compare(x.string(), y);
}
Tidigare skulle anropet till compare ha försökt specialisera funktionsmallen compare med hjälp av ett String mallargument för T. Det skulle inte generera en giltig specialisering eftersom String det är en abstrakt klass. Den enda livskraftiga kandidaten skulle ha varit compare(const Node&, const Node&). Under C++20 sker dock inte kontrollen av den abstrakta klasstypen förrän funktionen anropas. Specialiseringen compare(String, String) läggs därför till i uppsättningen med livskraftiga kandidater och väljs som den bästa kandidaten eftersom konverteringen från const String& till String är en bättre konverteringssekvens än konverteringen från const String& till const Node&.
Under C++20 är en möjlig korrigering för det här exemplet att använda begrepp. det vill: ändra definitionen av compare till:
template<typename T>
int compare(T x, T y) requires !std::is_abstract_v<T>
{
    return x < y ? -1 : (x > y ? 1 : 0);
}
Eller om C++-begrepp inte är tillgängliga kan du återgå till SFINAE:
template<typename T, std::enable_if_t<!std::is_abstract_v<T>, int> = 0>
int compare(T x, T y)
{
    return x < y ? -1 : (x > y ? 1 : 0);
}
Stöd för P0960R3 – tillåt initiering av aggregeringar från en parenteslista med värden
C++20 P0960R3 lägger till stöd för att initiera en aggregering med en parentesiserad initialiserarlista. Följande kod är till exempel giltig i C++20:
struct S {
    int i;
    int j;
};
S s(1, 2);
Det mesta av den här funktionen är additiv, dvs. kod kompilerar nu som inte kompilerats tidigare. Men det ändrar beteendet hos std::is_constructible. I C++17-läge misslyckas detta static_assert , men i C++20-läge lyckas det:
static_assert(std::is_constructible_v<S, int, int>, "Assertion failed!");
Om du använder det här attributet för kontroll av överbelastningslösning kan det leda till en ändring i beteendet mellan C++17 och C++20.
Överbelastningslösning med funktionsmallar
Tidigare tillät kompilatorn viss kod att kompileras under /permissive- som inte bör kompileras. Det ledde till att kompilatorn kallade på fel funktion, vilket ledde till en ändring i programkörningen.
int f(int);
namespace N
{
    using ::f;
    template<typename T>
    T f(T);
}
template<typename T>
void g(T&& t)
{
}
void h()
{
    using namespace N;
    g(f);
}
Anropet till g använder en överlagringsuppsättning som innehåller två funktioner, ::f och N::f. Eftersom N::f är en funktionsmall bör kompilatorn behandla funktionsargumentet som en icke-inducerad kontext. Det innebär att i det här fallet bör anropet till g misslyckas, eftersom kompilatorn inte kan härleda en typ för mallparametern T. Kompilatorn tog tyvärr inte bort det faktum att den redan hade bestämt att ::f det var en bra matchning för funktionsanropet. I stället för att avge ett fel skulle kompilatorn generera kod för att anropa g med ::f som argument.
Eftersom det i många fall är vad användaren förväntar sig att använda ::f som funktionsargument genererar vi bara ett fel om koden kompileras med /permissive-.
Migrera från /await till C++20 coroutines
Standard-C++20-coroutines är nu på som standard under /std:c++20 och /std:c++latest. De skiljer sig från Coroutines TS och stödet under alternativet /await . Att migrera från /await till standardkoroutines kan kräva vissa källändringar.
Nyckelord som inte är standard
De gamla await nyckelorden och yield stöds inte i C++20-läge. Koden måste användas co_await och co_yield i stället. Standardläget tillåter inte heller användning av return i en coroutine. Varje return i en coroutine måste använda co_return.
// /await
task f_legacy() {
    ...
    await g();
    return n;
}
// /std:c++latest
task f() {
    ...
    co_await g();
    co_return n;
}
Typer av första avstängning/slutlig avstängning
Under /await kan funktionerna promise initial och suspend deklareras som att returnera bool. Det här beteendet är inte standard. I C++20 måste dessa funktioner returnera en väntande klasstyp, ofta en av de triviala väntande typerna: std::suspend_always om funktionen tidigare returnerade trueeller std::suspend_never om den returnerade false.
// /await
struct promise_type_legacy {
    bool initial_suspend() noexcept { return false; }
    bool final_suspend() noexcept { return true; }
    ...
};
// /std:c++latest
struct promise_type {
    auto initial_suspend() noexcept { return std::suspend_never{}; }
    auto final_suspend() noexcept { return std::suspend_always{}; }
    ...
};
Typ av yield_value
I C++20 måste promise-funktionen yield_value returnera en väntande typ. I /await läge tilläts yield_value-funktionen att returnera void, och den skulle alltid pausas. Sådana funktioner kan ersättas med en funktion som returnerar std::suspend_always.
// /await
struct promise_type_legacy {
    ...
    void yield_value(int x) { next = x; };
};
// /std:c++latest
struct promise_type {
    ...
    auto yield_value(int x) { next = x; return std::suspend_always{}; }
};
Undantagshanteringsfunktion
              /await stöder en löftestyp utan någon undantagshanteringsfunktion eller en undantagshanteringsfunktion med namnet set_exception som tar en std::exception_ptr. I C++20 måste löftestypen ha en funktion med namnet unhandled_exception som inte tar några argument. Undantagsobjektet kan hämtas från std::current_exception om det behövs.
// /await
struct promise_type_legacy {
    void set_exception(std::exception_ptr e) { saved_exception = e; }
    ...
};
// /std:c++latest
struct promise_type {
    void unhandled_exception() { saved_exception = std::current_exception(); }
    ...
};
Härledda returtyper av koroutiner stöds inte
C++20 stöder inte coroutines med en returtyp som innehåller en platshållartyp som auto. Returtyper av koroutiner måste deklareras uttryckligen. Under /awaitinnebär dessa härledde typer alltid en experimentell typ och kräver inkludering av en rubrik som definierar den typ som krävs: En av std::experimental::task<T>, std::experimental::generator<T>eller std::experimental::async_stream<T>.
// /await
auto my_generator() {
    ...
    co_yield next;
}
// /std:c++latest
#include <experimental/generator>
std::experimental::generator<int> my_generator() {
    ...
    co_yield next;
}
Returtyp av return_value
Returtypen för promise-funktionen return_value måste vara void. I /await läget kan returtypen vara vad som helst och ignoreras. Den här diagnostiken kan hjälpa dig att upptäcka subtila fel, till exempel när författaren felaktigt antar att returvärdet return_value returneras till en anropare.
// /await
struct promise_type_legacy {
    ...
    int return_value(int x) { return x; } // incorrect, the return value of this function is unused and the value is lost.
};
// /std:c++latest
struct promise_type {
    ...
    void return_value(int x) { value = x; }; // save return value
};
Beteende för konvertering av returobjekt
Om den deklarerade returtypen för en coroutine inte matchar returtypen för promise-funktionen get_return_object konverteras objektet som returneras från get_return_object till coroutinens returtyp. Under /await utförs den här konverteringen tidigt, innan en coroutine har möjlighet att köras. I /std:c++20 eller /std:c++latestutförs den här konverteringen när värdet returneras till anroparen. Det möjliggör för coroutines att, när de inte pausas vid den första uppehållspunkten, använda det objekt som returneras av get_return_object inom coroutine kroppen.
Coroutine promise-parametrar
I C++20 försöker kompilatorn skicka coroutine-parametrarna (om några) till en konstruktor av löftestypen. Om det misslyckas försöker den igen med en standardkonstruktor. I /await läget användes endast standardkonstruktorn. Den här ändringen kan leda till en skillnad i beteende om löftet har flera konstruktorer. Eller om det finns en konvertering från en coroutine-parameter till löftestypen.
struct coro {
    struct promise_type {
        promise_type() { ... }
        promise_type(int x) { ... }
        ...
    };
};
coro f1(int x);
// Under /await the promise gets constructed using the default constructor.
// Under /std:c++latest the promise gets constructed using the 1-argument constructor.
f1(0);
struct Object {
template <typename T> operator T() { ... } // Converts to anything!
};
coro f2(Object o);
// Under /await the promise gets constructed using the default constructor
// Under /std:c++latest the promise gets copy- or move-constructed from the result of
// Object::operator coro::promise_type().
f2(Object{});
              /permissive- och C++20-moduler är aktiverade som standard under /std:c++20
Stöd för C++20-moduler är aktiverat som standard under /std:c++20 och /std:c++latest. Mer information om den här ändringen och scenarier där module och import behandlas villkorligt som nyckelord finns i Stöd för Standard C++20-moduler med MSVC i Visual Studio 2019 version 16.8.
Som en förutsättning för modulstöd permissive- aktiveras nu när /std:c++20 eller /std:c++latest har angetts. Mer information finns i /permissive-.
För kod som tidigare kompilerats under /std:c++latest och kräver icke-kompatibla kompilatorbeteenden /permissive kan anges för att inaktivera strikt efterlevnadsläge i kompilatorn. Kompilatoralternativet måste visas efter /std:c++latest i kommandoradsargumentlistan. Men /permissive resulterar i ett fel om modulanvändning upptäcks.
fel C1214: Moduler är i konflikt med icke-standardbeteende sådant som begärts genom 'alternativet'
De vanligaste värdena för alternativet är:
| Alternativ | Beskrivning | 
|---|---|
| /Zc:twoPhase- | Tvåfasnamnsökning krävs för C++20-moduler och detta antyds av /permissive-. | 
| /Zc:hiddenFriend- | Standardregler för sökning efter dolda vännamn krävs för C++20-moduler och underförstådda av /permissive-. | 
| /Zc:lambda- | Standardbearbetning av lambda krävs för C++20-moduler och är underförstådd i /std:c++20läge eller senare. | 
| /Zc:preprocessor- | Den överensstämmande förprocessorn krävs endast för användning och skapande av C++20-huvudenhet. Namngivna moduler kräver inte det här alternativet. | 
Alternativet /experimental:module krävs fortfarande för att använda modulerna std.* som levereras med Visual Studio, eftersom de inte är standardiserade ännu.
Alternativet /experimental:module innebär också /Zc:twoPhase, /Zc:lambda och /Zc:hiddenFriend. Tidigare kunde kod som kompilerats med Moduler ibland kompileras med /Zc:twoPhase- om modulen bara förbrukades. Det här beteendet stöds inte längre.
Anpassningsförbättringar i Visual Studio 2019 version 16.9
Kopieringsinitiering av en temporär variabel vid direkt initiering av referens
Kärnarbetsgruppens problem CWG 2267 handlade om en inkonsekvens mellan en parentesiserad initieringslista och en klammerparenteserad initialiserarlista. Upplösningen harmoniserar de två formerna.
Visual Studio 2019 version 16.9 implementerar det ändrade beteendet i alla /std kompilatorlägen. Men eftersom det kan vara en källbrytande ändring stöds den bara om koden kompileras med hjälp /permissive-av .
Det här exemplet visar beteendeförändringen:
struct A { };
struct B {
    explicit B(const A&);
};
void f()
{
    A a;
    const B& b1(a);     // Always an error
    const B& b2{ a };   // Allowed before resolution to CWG 2267 was adopted: now an error
}
Destruktoregenskaper och potentiellt konstruerade underobjekt
Core Working Group-ärende CWG 2336 täcker en utelämning gällande implicita undantagsspecifikationer för destruktorer i klasser som har virtuella basklasser. Utelämnandet innebar att en destructor i en härledd klass kunde ha en svagare undantagsspecifikation än en basklass, om den basen var abstrakt och hade en virtual bas.
Visual Studio 2019 version 16.9 implementerar det ändrade beteendet i alla /std kompilatorlägen.
Det här exemplet visar hur tolkningen ändrades:
class V {
public:
    virtual ~V() noexcept(false);
};
class B : virtual V {
    virtual void foo () = 0;
    // BEFORE: implicitly defined virtual ~B() noexcept(true);
    // AFTER: implicitly defined virtual ~B() noexcept(false);
};
class D : B {
    virtual void foo ();
    // implicitly defined virtual ~D () noexcept(false);
};
Före den här ändringen var Bden implicit definierade destruktor för noexcept , eftersom endast potentiellt konstruerade underobjekt beaktas. Och basklassen V är inte en potentiellt konstruerad underobjekt eftersom den är en virtual bas och B är abstrakt. Basklassen V är dock ett potentiellt konstruerat delobjekt av klassen D, så är D::~D bestämt att vara noexcept(false), vilket leder till en deriverad klass med en svagare undantagsspecifikation än dess bas. Den här tolkningen är osäker. Det kan leda till felaktigt körbeteende om ett undantag kastas från destruktorn i en klass som är härledd från B.
Med den här ändringen kan en destructor också utlösas om den har en virtuell destructor och alla virtuella basklasser har en potentiellt utlösande destructor.
Liknande typer och referensbindningar
Kärnarbetsgruppsfrågan CWG 2352 handlar om en inkonsekvens mellan referensbindningsreglerna och ändringar av typlikhet. Inkonsekvensen infördes i tidigare defektrapporter (till exempel CWG 330). Detta påverkade Visual Studio 2019-versionerna 16.0 till och med 16.8.
Från och med Visual Studio 2019 version 16.9 kan kod som tidigare har bundit en referens till en temporär i Visual Studio 2019 version 16.0 till 16.8 nu binda direkt när de berörda typerna endast skiljer sig åt genom cv-kvalificerare.
Visual Studio 2019 version 16.9 implementerar det ändrade beteendet i alla /std kompilatorlägen. Det är potentiellt en källbrytande ändring.
Se Referenser till typer med matchningsfel för cv-kvalificerare för en relaterad ändring.
Det här exemplet visar det ändrade beteendet:
int *ptr;
const int *const &f() {
    return ptr; // Now returns a reference to 'ptr' directly.
    // Previously returned a reference to a temporary and emitted C4172
}
Uppdateringen kan ändra programbeteendet som förlitade sig på en tillfällig lösning.
int func() {
    int i1 = 13;
    int i2 = 23;
    
    int* iptr = &i1;
    int const * const&  iptrcref = iptr;
    // iptrcref is a reference to a pointer to i1 with value 13.
    if (*iptrcref != 13)
    {
        return 1;
    }
    
    // Now change what iptr points to.
    // Prior to CWG 2352 iptrcref should be bound to a temporary and still points to the value 13.
    // After CWG 2352 it is bound directly to iptr and now points to the value 23.
    iptr = &i2;
    if (*iptrcref != 23)
    {
        return 1;
    }
    return 0;
}
              /Zc:twoPhase och ändring av /Zc:twoPhase--alternativets beteende
Normalt fungerar MSVC-kompilatoralternativen enligt principen att den senaste som visas vinner. Tyvärr var det inte fallet med /Zc:twoPhase alternativen och /Zc:twoPhase- . Dessa alternativ var "klibbiga", så senare alternativ kunde inte åsidosätta dem. Till exempel:
cl /Zc:twoPhase /permissive a.cpp
I det här fallet aktiverar det första /Zc:twoPhase alternativet strikt tvåfasnamnsökning. Det andra alternativet är avsett att inaktivera strikt efterlevnadsläge (det är motsatsen till /permissive-), men det inaktiverade inte /Zc:twoPhase.
Visual Studio 2019 version 16.9 ändrar det här beteendet i alla /std kompilatorlägen. 
              /Zc:twoPhase och /Zc:twoPhase- är inte längre "klibbiga" och senare alternativ kan åsidosätta dem.
Explicita noexcept-specifiers för destruktormallar
Kompilatorn accepterade tidigare en destruktormall som deklarerats med en undantagsspecifikation som inte utlöses men som definierats utan en explicit noexcept-specifier. Den implicita undantagsspecifikationen för en destructor beror på egenskaperna för klassen – egenskaper som kanske inte är kända vid definitionspunkten för en mall. C++ Standard kräver också det här beteendet: Om en destruktor deklareras utan en noexcept-specifier har den en implicit undantagsspecifikation och ingen annan deklaration av funktionen kan ha en noexcept-specificer.
Visual Studio 2019 version 16.9 ändras till att följa beteendet i alla /std kompilatorlägen.
Det här exemplet visar ändringen i kompilatorbeteendet:
template <typename T>
class B {
    virtual ~B() noexcept; // or throw()
};
template <typename T>
B<T>::~B() { /* ... */ } // Before: no diagnostic.
// Now diagnoses a definition mismatch. To fix, define the implementation by 
// using the same noexcept-specifier. For example,
// B<T>::~B() noexcept { /* ... */ }
Skriv om uttryck i C++20
Sedan Visual Studio 2019 version 16.2, under /std:c++latest, har kompilatorn accepterat kod som det här exemplet:
#include <compare>
struct S {
    auto operator<=>(const S&) const = default;
    operator bool() const;
};
bool f(S a, S b) {
    return a < b;
}
Kompilatorn anropar dock inte den jämförelsefunktion som författaren kan förvänta sig. Koden ovan borde ha skrivits om a < b som (a <=> b) < 0. Kompilatorn använde i stället den operator bool() användardefinierade konverteringsfunktionen och jämförde bool(a) < bool(b). I Visual Studio 2019 version 16.9 och senare skriver kompilatorn om uttrycket med det förväntade rymdskeppsoperatoruttrycket.
Ändring av icke-bakåtkompatibel källa
Korrekt tillämpning av konverteringar på omskrivna uttryck har en annan effekt: Kompilatorn diagnostiserar också tvetydigheter från försök att skriva om uttrycket. Tänk på det här exemplet:
struct Base {
    bool operator==(const Base&) const;
};
struct Derived : Base {
    Derived();
    Derived(const Base&);
    bool operator==(const Derived& rhs) const;
};
bool b = Base{} == Derived{};
I C++17 skulle den här koden accepteras på grund av härledd-till-bas-konverteringen av Derived på höger sida av uttrycket. I C++20 läggs även den syntetiserade uttryckskandidaten till: Derived{} == Base{}. På grund av reglerna i standarden om vilken funktion som vinner baserat på konverteringar visar det sig att valet mellan Base::operator== och Derived::operator== är osäkert. Eftersom konverteringssekvenserna i de två uttrycken inte är bättre eller sämre än varandra resulterar exempelkoden i en tvetydighet.
Lös tvetydigheten genom att lägga till en ny kandidat som inte omfattas av de två konverteringssekvenserna:
bool operator==(const Derived&, const Base&);
Brytande förändring vid körning
På grund av operatörens omskrivningsregler i C++20 är det möjligt för överbelastningslösning att hitta en ny kandidat, som annars inte skulle hittas i ett lägre språkläge. Och den nya kandidaten kan vara en bättre matchning än den äldre kandidaten. Tänk på det här exemplet:
struct iterator;
struct const_iterator {
  const_iterator(const iterator&);
  bool operator==(const const_iterator &ci) const;
};
struct iterator {
  bool operator==(const const_iterator &ci) const { return ci == *this; }
};
I C++17 är ci == *thisden enda kandidaten för const_iterator::operator== . Det är en matchning eftersom *this genomgår en förvandling från härledd till bas till const_iterator. I C++20 läggs en annan omskriven kandidat till: *this == ci, som anropar iterator::operator==. Den här kandidaten kräver inga konverteringar, så det är en bättre matchning än const_iterator::operator==. Problemet med den nya kandidaten är att det är den funktion som för närvarande definieras, så den nya semantiken i funktionen orsakar en oändligt rekursiv definition av iterator::operator==.
För att hjälpa till med kod som exemplet implementerar kompilatorn en ny varning:
$ cl /std:c++latest /c t.cpp
t.cpp
t.cpp(8): warning C5232: in C++20 this comparison calls 'bool iterator::operator ==(const const_iterator &) const' recursively
Åtgärda koden genom att vara explicit om vilken konvertering som ska användas:
struct iterator {
  bool operator==(const const_iterator &ci) const { return ci == static_cast<const const_iterator&>(*this); }
};
Anpassningsförbättringar i Visual Studio 2019 version 16.10
Fel överbelastning valdes för kopieringsinitialisering av klassen
Med den här exempelkoden:
struct A { template <typename T> A(T&&); };
struct B { operator A(); };
struct C : public B{};
void f(A);
f(C{});
Tidigare versioner av kompilatorn skulle felaktigt konvertera argumentet av f från typ C till en A genom att använda den mallade konverterande konstruktorn för A. Standard C++ kräver användning av konverteringsoperatorn B::operator A i stället. I Visual Studio 2019-versionen 16.10 och senare ändras beteendet för överlagringsupplösning för att använda rätt överlagring.
Den här ändringen kan också korrigera den valda överbelastningen i vissa andra situationer:
struct Base 
{
    operator char *();
};
struct Derived : public Base
{
    operator bool() const;
};
void f(Derived &d)
{
    // Implicit conversion to bool previously used Derived::operator bool(), now uses Base::operator char*.
    // The Base function is preferred because operator bool() is declared 'const' and requires a qualification
    // adjustment for the implicit object parameter, while the Base function does not.
    if (d)
    {
        // ...
    }
}
Felaktig parsning av flyttalsliteraler
I Visual Studio 2019 version 16.10 och senare parsas flyttalsliteraler baserat på deras faktiska typ. Tidigare versioner av kompilatorn parsade alltid en flyttalsliteral som om den hade typ double och konverterade sedan resultatet till den faktiska typen. Det här beteendet kan leda till felaktig avrundning och avvisande av giltiga värden:
// The binary representation is '0x15AE43FE' in VS2019 16.9
// The binary representation is '0x15AE43FD' in VS2019 16.10
// You can use 'static_cast<float>(7.038531E-26)' if you want the old behavior.
float f = 7.038531E-26f;
Felaktig deklarationspunkt
Tidigare versioner av kompilatorn kunde inte kompilera självrefererande kod som det här exemplet:
struct S {
    S(int, const S*);
    int value() const;
};
S s(4, &s);
Kompilatorn deklarerade inte variabeln s förrän den parsade hela deklarationen, inklusive konstruktorargumenten. Sökningen s av i konstruktorns argumentlista skulle misslyckas. I Visual Studio 2019 version 16.10 och senare kompileras nu det här exemplet korrekt.
Tyvärr kan den här ändringen bryta befintlig kod, som i det här exemplet:
S s(1, nullptr); // outer s
// ...
{
   S s(s.value(), nullptr); // inner s
}
I tidigare versioner av kompilatorn, när den söker upp s i konstruktorargumenten för den "inre" deklarationen av s, hittar den den tidigare deklarationen ("yttre" s) och koden kompileras. Från och med version 16.10 genererar kompilatorn varning C4700 i stället. Det beror på att kompilatorn nu deklarerar "inre" s innan konstruktorargumenten parsas. Sökningen s hittar därför den "inre" s, som inte har initierats ännu.
Uttryckligen specialiserad medlem i en klassmall
Tidigare versioner av kompilatorn markerade felaktigt en explicit specialisering av en klassmallsmedlem som inline om den också definierades i den primära mallen. Det här beteendet innebar att kompilatorn ibland avvisade kod som överensstämmer. I Visual Studio 2019 version 16.10 och senare är en explicit specialisering inte längre implicit markerad som inline i /permissive- läget. Tänk på det här exemplet:
Källfil s.h:
// s.h
template<typename T>
struct S {
    int f() { return 1; }
};
template<> int S<int>::f() { return 2; }
Källfil s.cpp:
// s.cpp
#include "s.h"
Källfil main.cpp:
// main.cpp
#include "s.h"
int main()
{
}
Om du vill åtgärda länkfelet i exemplet ovan lägger du uttryckligen till inlineS<int>::f:
template<> inline int S<int>::f() { return 2; }
Härledd mangling för returtypsnamn
I Visual Studio 2019 version 16.10 och senare ändrade kompilatorn hur den genererar manglade namn för funktioner som har härlett returtyper. Tänk till exempel på följande funktioner:
auto f() { return 0; }
auto g() { []{}; return 0; }
Tidigare versioner av kompilatorn skulle generera dessa namn för länkaren:
f: ?f@@YAHXZ -> int __cdecl f(void)
g: ?g@@YA@XZ -> __cdecl g(void)
Överraskande nog skulle returtypen från g utelämnas på grund av annat semantiskt beteende orsakat av den lokala lambdan i funktionskroppen. Den här inkonsekvensen gjorde det svårt att implementera exporterade funktioner som har en härledd returtyp: Modulgränssnittet kräver information om hur brödtexten i en funktion kompilerades. Den behöver informationen för att skapa en funktion på importsidan som korrekt kan länka till definitionen.
Kompilatorn utelämnar nu returtypen för en funktion av härledd returtyp. Det här beteendet överensstämmer med andra större implementeringar. Det finns ett undantag för funktionsmallar: Den här versionen av kompilatorn introducerar ett nytt manglat namnbeteende för funktionsmallar som har en härledd returtyp:
template <typename T>
auto f(T) { return 1; }
template <typename T>
decltype(auto) g(T) { return 1.; }
int (*fp1)(int) = &f;
double (*fp2)(int) = &g;
De manglade namnen för auto och decltype(auto) visas nu i binärfilen, inte den härledde returtypen:
f: ??$f@H@@YA?A_PH@Z -> auto __cdecl f<int>(int)
g: ??$g@H@@YA?A_TH@Z -> decltype(auto) __cdecl g<int>(int)
Tidigare versioner av kompilatorn skulle inkludera den härledde returtypen som en del av signaturen. När kompilatorn inkluderade returtypen i det manglade namnet kan det orsaka problem med länkaren. Vissa annars välformulerad scenarier skulle bli tvetydiga för länkaren.
Det nya kompilatorns beteende kan orsaka en ändring som bryter binär kompatibilitet. Tänk på det här exemplet:
Källfil a.cpp:
// a.cpp
auto f() { return 1; }
Källfil main.cpp:
// main.cpp
int f();
int main() { f(); }
I versioner före version 16.10 skapade kompilatorn ett namn för auto f() som såg ut som int f(), även om de är semantiskt distinkta funktioner. Det innebär att exemplet kompileras. För att åtgärda problemet, förlita dig inte på auto i den ursprungliga definitionen av f. Skriv det i stället som int f(). Eftersom funktioner som har härlett returtyper alltid kompileras minimeras ABI-konsekvenserna.
Varning för ignorerat nodiscard attribut
Tidigare versioner av kompilatorn skulle i tysthet ignorera vissa användningsområden för ett nodiscard attribut. De ignorerade attributet om det var i en syntaktisk position som inte gällde för funktionen eller klassen som deklarerades. Till exempel:
static [[nodiscard]] int f() { return 1; }
I Visual Studio 2019 version 16.10 och senare genererar kompilatorn nivå 4-varning C5240 i stället:
a.cpp(1): warning C5240: 'nodiscard': attribute is ignored in this syntactic position
Åtgärda problemet genom att flytta attributet till rätt syntaktisk position:
[[nodiscard]] static int f() { return 1; }
Varning för include direktiv med systemhuvudnamn i modulens purview
I Visual Studio 2019 version 16.10 och senare genererar kompilatorn en varning för att förhindra ett vanligt fel vid redigering av modulgränssnitt. Om du inkluderar en standardbiblioteksrubrik efter en export module instruktion genererar kompilatorn varning C5244. Här är ett exempel:
export module m;
#include <vector>
export
void f(std::vector<int>);
Utvecklaren hade förmodligen inte för avsikt att modulen m skulle äga innehållet i <vector>. Kompilatorn genererar nu en varning som hjälper dig att hitta och åtgärda problemet:
m.ixx(2): warning C5244: '#include <vector>' in the purview of module 'm' appears erroneous. Consider moving that directive before the module declaration, or replace the textual inclusion with an "import <vector>;".
m.ixx(1): note: see module 'm' declaration
Åtgärda problemet genom att flytta #include <vector> före export module m;:
#include <vector>
export module m;
export
void f(std::vector<int>);
Varning för oanvända interna länkfunktioner
I Visual Studio 2019 version 16.10 och senare varnar kompilatorn i fler situationer där en orefererad funktion med intern länkning har tagits bort. Tidigare versioner av kompilatorn genererar varning C4505 för följande kod:
static void f() // warning C4505: 'f': unreferenced function with internal linkage has been removed
{
}
Kompilatorn varnar nu även för orefererade auto funktioner och orefererade funktioner i anonyma namnområden. Den genererar en off-by-default varning C5245 för båda följande funktioner:
namespace
{
    void f1() // warning C5245: '`anonymous-namespace'::f1': unreferenced function with internal linkage has been removed
    {
    }
}
auto f2() // warning C5245: 'f2': unreferenced function with internal linkage has been removed
{
    return []{ return 13; };
}
Varning för borttagande av klammerparenteser
I Visual Studio 2019 version 16.10 och senare varnar kompilatorn för initieringslistor som inte använder klammerparenteser för underobjekt. Kompilatorn avger varning C5246 avstängd som standard.
Här är ett exempel:
struct S1 {
  int i, j;
};
struct S2 {
   S1 s1;
   int k;
};
S2 s2{ 1, 2, 3 }; // warning C5246: 'S2::s1': the initialization of a subobject should be wrapped in braces
Åtgärda problemet genom att omsluta initieringen av underobjektet i klammerparenteser:
S2 s2{ { 1, 2 }, 3 };
Identifiera korrekt om ett const objekt inte har initierats
I Visual Studio 2019 version 16.10 och senare genererar kompilatorn nu felet C2737 när du försöker definiera ett const objekt som inte är helt initierat:
struct S {
   int i;
   int j = 2;
};
const S s; // error C2737: 's': const object must be initialized
Tidigare versioner av kompilatorn tillät den här koden att kompileras, även om S::i den inte har initierats.
Åtgärda problemet genom att initiera alla medlemmar innan du skapar en const instans av ett objekt:
struct S {
   int i = 1;
   int j = 2;
};
Anpassningsförbättringar i Visual Studio 2019 version 16.11
              /std:c++20 kompilatorläge
I Visual Studio 2019 version 16.11 och senare stöder /std:c++20 kompilatorn nu kompilatorläget. Tidigare var C++20-funktioner endast tillgängliga i /std:c++latest läge i Visual Studio 2019. C++20-funktioner som ursprungligen krävde /std:c++latest läge fungerar nu i /std:c++20 läge eller senare i de senaste versionerna av Visual Studio.