Share via


Vylepšení shody jazyka C++, změny chování a opravy chyb v sadě Visual Studio 2019

Microsoft C/C++ v sadě Visual Studio (MSVC) vylepšuje vylepšení shody a opravy chyb v každé verzi. Tento článek uvádí vylepšení podle hlavní verze a potom podle verze. Pokud chcete přejít přímo na změny konkrétní verze, použijte následující seznam v tomto článku.

Tento dokument obsahuje seznam změn v sadě Visual Studio 2019. Průvodce změnami v sadě Visual Studio 2022 najdete v tématu Vylepšení shody jazyka C++ v sadě Visual Studio 2022. Změny v sadě Visual Studio 2017 najdete v tématu Vylepšení shody jazyka C++ v sadě Visual Studio 2017. Úplný seznam předchozích vylepšení shody najdete v tématu Visual C++ What's New 2003 až 2015.

Vylepšení shody v sadě Visual Studio 2019 RTW (verze 16.0)

Visual Studio 2019 RTW obsahuje následující vylepšení shody, opravy chyb a změny chování v kompilátoru Microsoft C++.

Poznámka:

Funkce C++20 byly k dispozici pouze v /std:c++latest režimu v sadě Visual Studio 2019, dokud nebyla implementace C++20 považována za dokončenou. Visual Studio 2019 verze 16.11 zavádí režim kompilátoru /std:c++20 . V tomto článku teď funkce, které původně vyžadoval /std:c++latest režim, fungují v /std:c++20 režimu nebo novějších v nejnovějších verzích sady Visual Studio. Aktualizovali jsme dokumentaci, aby se zmínila /std:c++20, i když tato možnost nebyla při prvním vydání funkcí dostupná.

Vylepšená podpora modulů pro šablony a detekci chyb

Moduly jsou teď oficiálně ve standardu C++20. Vylepšená podpora byla přidána v sadě Visual Studio 2017 verze 15.9. Další informace naleznete v tématu Lepší podpora šablon a detekce chyb v modulech C++ s MSVC 2017 verze 15.9.

Upravená specifikace agregačního typu

Specifikace agregačního typu se změnila v jazyce C++20 (viz Zákaz agregací s konstruktory deklarovanými uživatelem). V sadě Visual Studio 2019 /std:c++latest (nebo /std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější) třída s libovolným konstruktorem deklarovaným uživatelem (například včetně deklarovaného = default konstruktoru nebo = delete) není agregace. Dříve pouze uživatelem poskytnuté konstruktory by diskvalifikovaly třídu od agregace. Tato změna přináší další omezení způsobu inicializace těchto typů.

Následující kód se zkompiluje bez chyb v sadě Visual Studio 2017, ale vyvolává chyby C2280 a C2440 v sadě Visual Studio 2019 v části /std:c++20 nebo /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

Částečná podpora pro operator <=>

P0515R3 C++20 zavádí trojcestný <=> operátor porovnání, označovaný také jako "operátor spaceship". Visual Studio 2019 verze 16.0 v /std:c++latest režimu zavádí částečnou podporu operátora vyvoláním chyb pro syntaxi, která je nyní zakázána. Například následující kód se zkompiluje bez chyb v sadě Visual Studio 2017, ale v sadě Visual Studio 2019 /std:c++20 pod nebo /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.
}

Chcete-li se vyhnout chybám, vložte mezeru do odsudové čáry před poslední úhel závorky: U<&S::operator<= > u;.

Odkazy na typy s neshodnými kvalifikátory cv

Poznámka:

Tato změna se týká jenom sady Visual Studio 2019 verze 16.0 až 16.8. Bylo vráceno od verze 16.9 sady Visual Studio 2019.

MsVC dříve povolila přímou vazbu odkazu z typu s neshodovanými kvalifikátory cv pod nejvyšší úrovní. Tato vazba by mohla umožňovat úpravy údajně constových dat odkazovaných odkazem.

Kompilátor pro Visual Studio 2019 verze 16.0 až 16.8 místo toho vytvoří dočasný, jak bylo vyžadováno standardem v té době. Později se standardní zpětně změnilo, aby předchozí chování sady Visual Studio 2017 a dříve správně a chování sady Visual Studio 2019 verze 16.0 až 16.8 bylo nesprávné. V důsledku toho se tato změna vrátila od sady Visual Studio 2019 verze 16.9.

Viz Podobné typy a odkazové vazby související změny.

Například v sadě Visual Studio 2017 se následující kód zkompiluje bez upozornění. V sadě Visual Studio 2019 verze 16.0 až 16.8 kompilátor vyvolá upozornění C4172. Od sady Visual Studio 2019 verze 16.9 se kód znovu zkompiluje bez upozornění:

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 z přetížené funkce

Argument reinterpret_cast není jedním z kontextů, ve kterých je povolena adresa přetížené funkce. Následující kód se zkompiluje bez chyb v sadě Visual Studio 2017, ale v sadě Visual Studio 2019 vyvolá chybu 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'
}

Pokud se chcete této chybě vyhnout, použijte povolené přetypování pro tento scénář:

int f(int);
int f(float);
using fp = int(*)(int);

int main()
{
    fp r = static_cast<fp>(&f); // or just &f;
}

Uzavření lambda

V jazyce C++14 nejsou typy uzavření lambda literály. Primárním důsledkem tohoto pravidla je, že lambda nemusí být přiřazena proměnné constexpr . Následující kód se zkompiluje bez chyb v sadě Visual Studio 2017, ale v sadě Visual Studio 2019 vyvolá chybu C2127:

int main()
{
    constexpr auto l = [] {}; // C2127 'l': illegal initialization of 'constexpr' entity with a non-constant expression
}

Chcete-li se této chybě vyhnout, odeberte constexpr kvalifikátor nebo jinak změňte režim shody na /std:c++17 nebo novější.

std::create_directory kódy selhání

Implementovali jsme P1164 z C++20 bezpodmínečně. Tyto změny se změní std::create_directory a zkontrolují, jestli už byl cíl v adresáři při selhání. Dříve byly všechny chyby typu ERROR_ALREADY_EXISTS převedeny na kódy typu success-but-directory-not-created.

operator<<(std::ostream, nullptr_t)

Na LWG 2221, přidáno operator<<(std::ostream, nullptr_t) pro zápis nullptr do datových proudů.

Další paralelní algoritmy

Nové paralelní verze is_sorted, , is_sorted_untilis_partitioned, set_differenceset_intersection, is_heapa is_heap_until.

Opravy atomické inicializace

P0883 "Oprava atomické inicializace" se změní std::atomic tak, aby inicializovala hodnotu obsaženou T místo výchozí inicializace. Oprava je povolená při použití Clang/LLVM se standardní knihovnou Microsoftu. V současné době je pro kompilátor Microsoft C++ zakázán jako alternativní řešení chyby při constexpr zpracování.

remove_cvref a remove_cvref_t

Implementovali vlastnosti remove_cvref a typy z P0550remove_cvref_t. Tím se z typu odeberou referenční ness a kvalifikace cv bez rozkladu funkcí a polí na ukazatele (na rozdíl od std::decay a std::decay_t).

Makra testů funkcí

P0941R2 – makra testů funkcí jsou dokončena s podporou __has_cpp_attribute. Makra testů funkcí jsou podporována ve všech standardních režimech.

Zakázání agregací s uživatelem deklarovanými konstruktory

C++20 P1008R1 – zakázání agregací s uživatelem deklarovanými konstruktory je dokončeno.

reinterpret_castconstexpr ve funkci

Funkce A reinterpret_cast je neplatná constexpr . Kompilátor Jazyka Microsoft C++ by dříve odmítl reinterpret_cast pouze v případě, že byl použit v constexpr kontextu. V sadě Visual Studio 2019 kompilátor ve všech režimech jazyka správně diagnostikuje reinterpret_cast definici constexpr funkce. Následující kód teď vytvoří C3615:

long long i = 0;
constexpr void f() {
    int* a = reinterpret_cast<int*>(i); // C3615: constexpr function 'f' cannot result in a constant expression
}

Chcete-li se této chybě vyhnout, odeberte constexpr modifikátor z deklarace funkce.

Oprava diagnostiky pro konstruktor rozsahu basic_string

V sadě Visual Studio 2019 basic_string konstruktor rozsahu již nepotlačí diagnostiku kompilátoru pomocí static_cast. Následující kód se zkompiluje bez upozornění v sadě Visual Studio 2017 bez ohledu na možnou ztrátu dat z wchar_tchar inicializace 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 správně vyvolá upozornění C4244. Chcete-li se upozornění vyhnout, můžete inicializovat std::string , jak je znázorněno v tomto příkladu:

std::wstring ws = L"Hello world";
std::string out;
for (wchar_t ch : ws)
{
    out.push_back(static_cast<char>(ch));
}

Nesprávná volání nebo +=-= pod /clr nebo /ZW jsou nyní správně zjištěna

V sadě Visual Studio 2017 byla zavedena chyba, která způsobila, že kompilátor bezobslužně ignoroval chyby a negeneroval žádný kód pro neplatná volání nebo +=-= pod /clr nebo /ZW. Následující kód se zkompiluje bez chyb v sadě Visual Studio 2017, ale v sadě Visual Studio 2019 správně vyvolá chybu C2845:

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.
}

Chcete-li se vyhnout chybě v tomto příkladu += , použijte operátor s metodou ToString() : s += E::e.ToString();.

Inicializátory pro vložené statické datové členy

V rámci inline a static constexpr inicializátorů jsou nyní správně zjištěny neplatné přístupy členů. Následující příklad se zkompiluje bez chyby v sadě Visual Studio 2017, ale v sadě Visual Studio 2019 v /std:c++17 režimu nebo novějším vyvolá chybu 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'.
};

Pokud se chcete této chybě vyhnout, deklarujte člena X::c jako chráněný:

struct X
{
    protected:
        static inline const int c = 1000;
};

Obnoveno C4800

MSVC používá k upozornění výkonu C4800 o implicitní převod na bool. Bylo to příliš hlučné a nebylo možné ho potlačit, což nás vedlo k jeho odebrání v sadě Visual Studio 2017. V průběhu životního cyklu sady Visual Studio 2017 jsme ale získali spoustu zpětné vazby k užitečným případům, které řeší. V sadě Visual Studio 2019 jsme společně s vysvětlujícím C4165 vrátili pečlivě přizpůsobenou verzi C4800. Obě tato upozornění se dají snadno potlačit: buď pomocí explicitního přetypování, nebo porovnáním s hodnotou 0 příslušného typu. C4800 je upozornění úrovně 4 mimo výchozí úroveň a C4165 je upozornění úrovně 3 mimo výchozí úroveň. Obě možnosti jsou zjistitelné pomocí možnosti kompilátoru /Wall .

Následující příklad vyvolá C4800 a C4165 pod /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?
}

Abyste se vyhnuli upozorněním v předchozím příkladu, můžete napsat kód takto:

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
}

Funkce člena místní třídy nemá tělo.

V sadě Visual Studio 2017 se upozornění C4822 vyvolá pouze v případě, že je explicitně nastavena možnost /w14822 kompilátoru. Nezobrazuje se s /Wallním . V sadě Visual Studio 2019 je C4822 ve výchozím nastavení upozornění, které umožňuje zjistitelnost v části /Wall bez nutnosti explicitně nastavit /w14822 .

void example()
{
    struct A
        {
            int boo(); // warning C4822: Local class member function doesn't have a body
        };
}

Těla šablony funkcí obsahující if constexpr příkazy

V sadě Visual Studio 2019 v /std:c++20 sadě Visual Studio 2019 nebo /std:c++latestv tělech funkcí šablony s if constexpr povolenými příkazy jsou povoleny další kontroly související s parsováním. Například v sadě Visual Studio 2017 následující kód vytvoří C7510 pouze v případě, že /permissive- je tato možnost nastavená. V sadě Visual Studio 2019 stejný kód vyvolává chyby i v případě, /permissive že je tato možnost nastavená:

// 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";
}

Chcete-li se vyhnout chybě, přidejte typename klíčové slovo do deklarace a: typename T::Type a;.

Vložený kód sestavení není ve výrazu lambda podporován.

Tým Microsoft C++ nedávno věděl o problému se zabezpečením, kdy použití inline-assembleru v rámci lambda mohlo vést k poškození (návratového ebp registru adres) za běhu. Útočník se zlými úmysly by mohl tento scénář využít. Vložený assembler je podporován pouze na platformě x86 a interakce mezi vložený assembler a zbytkem kompilátoru je špatná. Vzhledem k těmto faktům a povaze problému bylo nejbezpečnějším řešením tohoto problému zakázat vložený assembler v rámci výrazu lambda.

Jediným použitím vloženého assembleru ve výrazu lambda, který jsme našli v zástupné oblasti, bylo zachycení zpáteční adresy. V tomto scénáři můžete zachytit zpáteční adresu na všech platformách jednoduše pomocí vnitřní _ReturnAddress()kompilátoru .

Následující kód vytvoří C7553 v sadě Visual Studio 2017 15.9 a novějších verzích sady 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;
}

Pokud se chcete této chybě vyhnout, přesuňte kód sestavení do pojmenované funkce, jak je znázorněno v následujícím příkladu:

#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());
}

Ladění iterátoru a std::move_iterator

Funkce ladění iterátoru se naučila správně rozbalit std::move_iterator. Můžete se například std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) zapojit do memcpy rychlé cesty.

Opravy vynucení <klíčových slov xkeycheck.h>

Bylo opraveno vynucení standardní knihovny v <xkeycheck.h> pro makra, která nahrazují klíčová slova. Knihovna teď generuje skutečné klíčové slovo problému zjištěné místo obecné zprávy. Podporuje také klíčová slova C++20 a vyhýbá se tomu, aby intelliSense oznamovat náhodná klíčová slova jsou makra.

Typy alokátoru už nejsou zastaralé

std::allocator<void>std::allocator::difference_type a std::allocator::size_typeuž nejsou zastaralé.

Správné upozornění pro zužování převodů řetězců

Odebrali jsme z toho nevolaný static_caststd::string standard, který omylem potlačí upozornění C4244. Pokusí se volat std::string::string(const wchar_t*, const wchar_t*) nyní správně generovat C4244 o zúžení wchar_t na char.

Různé opravy <správnosti systému> souborů

  • Opravili jsme std::filesystem::last_write_time chybu při pokusu o změnu času posledního zápisu adresáře.
  • Konstruktor std::filesystem::directory_entry teď ukládá neúspěšný výsledek místo vyvolání výjimky při zadání neexistující cílové cesty.
  • Verze std::filesystem::create_directory 2 parametru byla změněna tak, aby volala verzi 1 parametru, protože základní CreateDirectoryExW funkce by se použila copy_symlink , když existing_p se jednalo o symlink.
  • std::filesystem::directory_iterator pokud se najde poškozený symlink, už se nezdaří.
  • std::filesystem::space nyní přijímá relativní cesty.
  • std::filesystem::path::lexically_relative již není zmatena koncovými lomítky hlášenými jako LWG 3096.
  • Pracovalo se CreateSymbolicLinkW s odmítnutím cest s lomítky v std::filesystem::create_symlink.
  • Vyřešili jsme funkci režimu delete odstranění POSIX, která existovala ve Windows 10 LTSB 1609, ale ve skutečnosti nemohla odstranit soubory.
  • Konstruktory std::boyer_moore_searcher a std::boyer_moore_horspool_searcher operátory přiřazení kopírování teď skutečně kopírují.

Paralelní algoritmy ve Windows 8 a novějších verzích

Knihovna paralelních algoritmů teď správně používá skutečnou WaitOnAddress rodinu ve Windows 8 a novějších verzích, a ne vždy používá windows 7 a starší falešné verze.

std::system_category::message() Mezery

std::system_category::message() nyní oříznou koncové prázdné znaky z vrácené zprávy.

std::linear_congruential_engine dělit nulou

Některé podmínky, které by způsobily std::linear_congruential_engine aktivaci dělení 0, byly opraveny.

Opravy pro zrušení přepsání iterátoru

Některé iterátor-unwrapping stroje byly poprvé vystaveny pro programátor-uživatele integrace v sadě Visual Studio 2017 15.8. Bylo popsáno v článku STL a opravy VS 2017 15.8 na blogu týmu C++. Tento stroj už nevybaluje iterátory odvozené od standardních iterátorů knihoven. Například uživatel, který je odvozen od std::vector<int>::iterator a snaží se přizpůsobit chování, nyní získá své přizpůsobené chování při volání standardních algoritmů knihovny, nikoli chování ukazatele.

Neuspořádaná funkce kontejneru reserve teď ve skutečnosti vyhrazuje pro N elementy, jak je popsáno v LWG 2156.

Zpracování času

  • Dříve se některé časové hodnoty, které byly předány do knihovny souběžnosti, condition_variable::wait_for(seconds::max())přeteče, například . Nyní opraveno, přetečení změnilo chování zdánlivě náhodného 29denního cyklu (při uint32_t milisekund přijatých základními rozhraními API Win32 přetečení).

  • Hlavička <ctime> nyní správně deklaruje timespec a timespec_get v oboru názvů stda také je deklaruje v globálním oboru názvů.

Různé opravy kontejnerů

  • Řada interních funkcí kontejneru standardní knihovny byla private vytvořená pro vylepšené prostředí IntelliSense. Další opravy označení členů podle private očekávání v pozdějších verzích MSVC.

  • Opravili jsme problémy se správností výjimek, které způsobily poškození kontejnerů založených na uzlech, jako listje , mapa unordered_map. propagate_on_container_copy_assignment Během operace nebo propagate_on_container_move_assignment opětovného přiřazení bychom uvolnili uzel sentinelu kontejneru se starým alokátorem, provedli jsme přiřazení POCCA/POCMA přes starý alokátor a potom se pokusili získat uzel sentinelu z nového alokátoru. Pokud toto přidělení selhalo, kontejner byl poškozen. Nepodařilo se ani zničit, protože vlastnictví uzlu sentinelu je pevná datová struktura invariantní. Tento kód byl opraven pro vytvoření nového uzlu sentinelu pomocí alokátoru zdrojového kontejneru před zničením existujícího uzlu sentinelu.

  • Kontejnery byly opraveny tak, aby vždy kopírovat/přesunout/prohodit alokátory podle propagate_on_container_copy_assignment, propagate_on_container_move_assignmenta propagate_on_container_swapdokonce i pro alokátory deklarované is_always_equal.

  • Přidání přetížení pro sloučení kontejnerů a extrakci členských funkcí, které přijímají kontejnery rvalue. Další informace najdete v tématu S0083 "Splicing Mapy And Sets"

std::basic_istream::read zpracování \ \r\n`` =>n'

std::basic_istream::read bylo opraveno, aby se při zpracování dočasně \r\n\n nezapisovalo do částí dodané vyrovnávací paměti. Tato změna přináší určitou výhodu výkonu získanou v sadě Visual Studio 2017 15.8 pro čtení větší než 4K. Vylepšení efektivity, aby se zabránilo třem virtuálním voláním na znak, ale stále existují.

std::bitset Konstruktor

Konstruktor std::bitset už nečte ty a nuly v obráceném pořadí pro velké bitové sady.

std::pair::operator= Regrese

Opravili jsme regresi v operátoru std::pair přiřazení zavedeném při implementaci LWG 2729 Chybějící SFINAE na std::pair::operator=;. Teď správně přijímá typy, na které se std::pair přepíná znovu.

Nededukované kontexty pro add_const_t

Opravili jsme chybu vedlejších typů, kdy add_const_t a související funkce měly být nededukovaný kontext. Jinými slovy, add_const_t měl by být alias pro typename add_const<T>::type, ne const T.

Vylepšení shody ve verzi 16.1

char8_t

P0482r6. C++20 přidá nový typ znaku, který se používá k reprezentaci jednotek kódu UTF-8. u8 řetězcové literály v jazyce C++20 mají typ const char8_t[N] místo const char[N], což byl dříve případ. Podobné změny byly navrženy pro standard C v N2231. Návrhy na char8_t nápravu zpětné kompatibility jsou uvedeny v P1423r3. Kompilátor Microsoft C++ přidává podporu pro char8_t v sadě Visual Studio 2019 verze 16.1 při zadávání možnosti kompilátoru /Zc:char8_t . Lze ho vrátit k chování jazyka C++17 prostřednictvím /Zc:char8_t-. Kompilátor EDG, který technologii IntelliSense zatím nepodporuje v sadě Visual Studio 2019 verze 16.1. Může se zobrazit nechutná chyba technologie IntelliSense, které nemají vliv na skutečnou kompilaci.

Příklad

const char* s = u8"Hello"; // C++17
const char8_t* s = u8"Hello"; // C++20

std::type_identity metafunkce a std::identity objekt funkce

P0887R1 type_identity. std::identity Zastaralé rozšíření šablony třídy bylo odebráno a nahrazeno metafunkcem C++20 std::type_identity a std::identity objektem funkce. Obě jsou dostupné pouze /std:c++latest v sadě Visual/std:c++20 Studio 2019 verze 16.11 a novější).

Následující příklad vytvoří upozornění na vyřazení C4996 pro std::identity (definované v <type_traits>) v sadě 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);

Následující příklad ukazuje, jak používat nové std::identity (definované v <funkčním>) společně s novým 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);

Kontroly syntaxe pro obecné lambda

Nový procesor lambda umožňuje některé syntaktické kontroly v režimu shody v obecných lambdach/std:c++latest/std:c++20( v sadě Visual Studio 2019 verze 16.11 a novější) nebo v jakémkoli jiném jazykovém režimu v /Zc:lambda sadě Visual Studio 2019 verze 16.9 nebo novější (dříve dostupné od /experimental:newLambdaProcessor sady Visual Studio 2019 verze 16.3).

Starší procesor lambda tento příklad zkompiluje bez upozornění, ale nový procesor lambda způsobí chybu C2760:

void f() {
    auto a = [](auto arg) {
        decltype(arg)::Type t; // C2760 syntax error: unexpected token 'identifier', expected ';'
    };
}

Tento příklad ukazuje správnou syntaxi, kterou teď kompilátor vynucuje:

void f() {
    auto a = [](auto arg) {
        typename decltype(arg)::Type t;
    };
}

Vyhledávání závislé na argumentech pro volání funkce

P0846R0 (C++20) Zvýšená schopnost najít šablony funkcí prostřednictvím vyhledávání závislého na argumentech pro výrazy volání funkce s explicitními argumenty šablony. Vyžaduje /std:c++latest (nebo /std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější).

Určená inicializace

P0329R4 (C++20) Vyhrazená inicializace umožňuje výběru konkrétních členů v agregované inicializaci pomocí Type t { .member = expr } syntaxe. Vyžaduje /std:c++latest (nebo /std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější).

Řazení převodu výčtu na pevný podkladový typ

Kompilátor nyní řadí převody výčtů podle N4800 11.3.3.2 Pořadí implicitních převodních sekvencí (4,2):

  • Převod, který propaguje výčet, jehož základní typ je pevně nastaven na jeho základní typ, je lepší než ten, který podporuje upřednostněný základní typ, pokud se oba typy liší.

Toto řazení převodu nebylo správně implementováno před sadou Visual Studio 2019 verze 16.1. Odpovídající chování může změnit chování řešení přetížení nebo odhalit nejednoznačnost, kde se dříve nezjistil.

Tato změna chování kompilátoru se vztahuje na všechny /std režimy a je to zdrojová i binární změna způsobující chybu.

Následující příklad ukazuje, jak se chování kompilátoru mění ve verzi 16.1 a novějších verzích:

#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{});
}

Nové a aktualizované funkce standardní knihovny (C++20)

  • starts_with() a ends_with() pro basic_string a basic_string_view.
  • contains() pro asociativní kontejnery.
  • remove(), remove_if() a unique() pro list a forward_list teď vrací size_type.
  • shift_left() a shift_right() přidaný do <algoritmu>.

Vylepšení shody ve verzi 16.2

noexceptconstexpr – funkce

constexpr funkce se už noexcept ve výchozím nastavení při použití ve výrazu konstanty nepovažují. Tato změna chování pochází z řešení základní pracovní skupiny (CWG) CWG 1351 a je povoleno v /permissive-. Následující příklad se zkompiluje v sadě Visual Studio 2019 verze 16.1 a starší, ale vytvoří C2338 v sadě Visual Studio 2019 verze 16.2:

constexpr int f() { return 0; }

int main() {
    static_assert(noexcept(f()), "f should be noexcept"); // C2338 in 16.2
}

Pokud chcete chybu opravit, přidejte noexcept výraz do deklarace funkce:

constexpr int f() noexcept { return 0; }

int main() {
    static_assert(noexcept(f()), "f should be noexcept");
}

Binární výrazy s různými typy výčtů

C++20 zastaralá obvyklé aritmetické převody na operandech, kde:

  • Jeden operand je typu výčtu a

  • druhý je jiného typu výčtu nebo typu s plovoucí desetinou čárkou.

Další informace najdete v tématu P1120R0.

V sadě Visual Studio 2019 verze 16.2 a novějším vytvoří následující kód upozornění úrovně 4 C5054, pokud je povolená možnost kompilátoru /std:c++latest (/std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější):

enum E1 { a };
enum E2 { b };
int main() {
    int i = a | b; // warning C5054: operator '|': deprecated between enumerations of different types
}

Pokud se chcete upozornění vyhnout, použijte static_cast k převodu druhého operandu:

enum E1 { a };
enum E2 { b };
int main() {
  int i = a | static_cast<int>(b);
}

Použití binární operace mezi výčtem a typem s plovoucí desetinou čárkou je nyní upozornění úrovně 1 C5055, pokud je povolena možnost kompilátoru /std:c++latest (/std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější):

enum E1 { a };
int main() {
  double i = a * 1.1;
}

Pokud se chcete upozornění vyhnout, použijte static_cast k převodu druhého operandu:

enum E1 { a };
int main() {
   double i = static_cast<int>(a) * 1.1;
}

Rovnost a relační porovnání polí

Rovnost a relační porovnání mezi dvěma operandy typu pole jsou v jazyce C++20 (P1120R0) zastaralé. Jinými slovy, operace porovnání mezi dvěma poli (navzdory podobnostem pořadí a rozsahu) je nyní upozorněním. V sadě Visual Studio 2019 verze 16.2 a novějším vytvoří následující kód upozornění C5056 úrovně 1, pokud je povolená možnost kompilátoru /std:c++latest (/std:c++20 v sadě Visual Studio 2019 verze 16.11 a novější):

int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (a == b) { return 1; } // warning C5056: operator '==': deprecated for array types
}

Abyste se vyhnuli upozornění, můžete porovnat adresy prvních prvků:

int main() {
    int a[] = { 1, 2, 3 };
    int b[] = { 1, 2, 3 };
    if (&a[0] == &b[0]) { return 1; }
}

Pokud chcete zjistit, jestli je obsah dvou polí stejný, použijte std::equal funkci:

std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));

Účinek definování operátoru vesmírné lodi na ==!=

Definice operátoru vesmírné lodi (<=>) už nebude přepisovat výrazy zahrnující == nebo != pokud operátor vesmírné lodi není označený jako = default (P1185R2). Následující příklad se zkompiluje v sadě Visual Studio 2019 RTW a verze 16.1, ale vytvoří C2678 v sadě Visual Studio 2019 verze 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
}

Pokud se chcete této chybě vyhnout, definujte ji nebo deklarujte operator== jako výchozí:

#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;
}

Vylepšení standardní knihovny

  • <charconv>to_chars() s pevnou/vědeckou přesností. (Obecná přesnost je aktuálně naplánována na verzi 16.4.)
  • P0020R6: atomic<float>, atomic<double>,atomic<long double>
  • P0463R1: endian
  • P0482R6: Podpora knihovny prochar8_t
  • P0600R1: [[nodiscard]] pro STL, část 1
  • P0653R2:to_address()
  • P0754R2: <verze>
  • P0771R1: noexcept Pro std::functionkonstruktor move

Komparátory const pro asociativní kontejnery

Kód pro vyhledávání a vložení do setsouboru , multisetmap, a multimap byl sloučen pro zmenšenou velikost kódu. Operace vložení teď volají menší než porovnání u trychtoru const porovnání stejným způsobem jako dříve provedené operace vyhledávání. Následující kód se zkompiluje v sadě Visual Studio 2019 verze 16.1 a starší, ale vyvolá C3848 v sadě Visual Studio 2019 verze 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);

}

Pokud se chcete této chybě vyhnout, proveďte relační operátor const:

struct Comparer  {
   bool operator() (K a, K b) const {
      return a.a < b.a;
   }
};

Vylepšení shody v sadě Visual Studio 2019 verze 16.3

Operátory extrakce datových proudů pro char* odebrání

Operátory extrakce datových proudů pro ukazatele na znaky byly odebrány a nahrazeny operátory extrakce pro pole znaků (podle P0487R1). WG21 považuje odebraná přetížení za nebezpečná. V /std:c++20 nebo /std:c++latest v režimu teď následující příklad vytvoří 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)
}

Pokud se chcete této chybě vyhnout, použijte operátor extrakce s proměnnou char[] :

#include <iostream>
#include <iomanip>

int main() {
    char x[42];
    std::cin >> std::setw(42);
    std::cin >> x;  // OK
}

Nová klíčová slova requires a concept

Nová klíčová slova requires a concept byly přidány do kompilátoru Microsoft C++. Pokud se pokusíte použít buď jeden jako identifikátor v /std:c++20 nebo /std:c++latest v režimu, kompilátor vyvolá C2059 k označení chyby syntaxe.

Konstruktory jako názvy typů nejsou zakázány.

Kompilátor už v tomto případě nepovažuje názvy konstruktorů za vložené názvy tříd: když se zobrazí v kvalifikovaném názvu za aliasem specializace šablony třídy. Dříve byly konstruktory použitelné jako název typu pro deklaraci jiných entit. Následující příklad nyní vytvoří C3646:

#include <chrono>

class Foo {
   std::chrono::milliseconds::duration TotalDuration{}; // C3646: 'TotalDuration': unknown override specifier
};

Pokud se chcete této chybě vyhnout, deklarujte TotalDuration ji, jak je znázorněno zde:

#include <chrono>

class Foo {
  std::chrono::milliseconds TotalDuration {};
};

Přísnější kontrola extern "C" funkcí

extern "C" Pokud byla funkce deklarována v různých oborech názvů, předchozí verze kompilátoru Microsoft C++ nekontrolovaly, zda byly deklarace kompatibilní. V sadě Visual Studio 2019 verze 16.3 a novější kompilátor kontroluje kompatibilitu. V /permissive- režimu následující kód generuje chyby C2371 a 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

Chcete-li se vyhnout chybám v předchozím příkladu, použijte bool místo BOOL konzistentně v obou deklaracích f.

Vylepšení standardní knihovny

Byly odebrány nestandardní hlavičky <stdexcpt.h> a <typeinfo.h> . Kód, který je obsahuje, by místo toho měl obsahovat standardní výjimku hlavičky <a <informace o typeinfo>.>

Vylepšení shody v sadě Visual Studio 2019 verze 16.4

Lepší vynucování dvoufázového vyhledávání názvů pro kvalifikované ID v /permissive-

Dvoufázové vyhledávání názvů vyžaduje, aby pro šablonu v okamžiku definice byly viditelné jiné než závislé názvy použité v těle šablony. Dříve se tyto názvy mohly najít při vytvoření instance šablony. Tato změna usnadňuje zápis přenosného a vyhovujícího kódu v MSVC pod příznakem /permissive- .

V sadě Visual Studio 2019 verze 16.4 se /permissive- sadou příznaků způsobí následující příklad chybu, protože N::f při definování šablony se nezobrazí f<T> :

template <class T>
int f() {
    return N::f() + T{}; // error C2039: 'f': is not a member of 'N'
}

namespace N {
    int f() { return 42; }
}

Tuto chybu je obvykle možné opravit zahrnutím chybějících hlaviček nebo deklarací funkcí nebo proměnných předávání, jak je znázorněno v následujícím příkladu:

namespace N {
    int f();
}

template <class T>
int f() {
    return N::f() + T{};
}

namespace N {
    int f() { return 42; }
}

Implicitní převod celočíselných konstantních výrazů na ukazatel null

Kompilátor MSVC teď implementuje problém CWG 903 v režimu shody (/permissive-). Toto pravidlo zakáže implicitní převod celočíselných konstantních výrazů (s výjimkou celočíselného literálu 0) na konstanty ukazatele null. Následující příklad vytvoří C2440 v režimu shody:

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 *'
}

Pokud chcete chybu opravit, použijte nullptr místo false. Literál 0 je stále povolený:

int* f(bool* p) {
    p = nullptr; // OK
    p = 0; // OK
    return nullptr; // OK
}

Standardní pravidla pro typy celočíselné literály

V režimu shody (povoleno), /permissive-MSVC používá standardní pravidla pro typy celočíselné literály. Desetinné literály jsou příliš velké, aby se vešly do dříve zadaného signed int typu unsigned int. Nyní jsou takové literály dány dalším největším signed celočíselnému typu, long long. Navíc literály s příponou "ll", které jsou příliš velké, aby se vešly do signed typu, mají daný typ unsigned long long.

Tato změna může vést ke generování různých diagnostik upozornění a rozdílům v chování pro aritmetické operace u literálů.

Následující příklad ukazuje nové chování v sadě Visual Studio 2019 verze 16.4. Proměnná i je teď typu unsigned int, takže se vyvolá upozornění. Bity proměnné j s vysokým pořadím jsou nastaveny na hodnotu 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
}

Následující příklad ukazuje, jak zachovat staré chování a vyhnout se upozorněním a změně chování za běhu:

void f(int r) {
int i = 2964557531u; // OK
long long j = (long long)0x8000000000000000ll >> r; // shift will keep high-order bits
}

Parametry funkce, které parametry stínové šablony

Kompilátor MSVC teď vyvolá chybu, když parametr funkce stínuje parametr šablony:

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);
}

Pokud chcete chybu opravit, změňte název jednoho z parametrů:

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);
}

Specializace typu poskytované uživatelem

V souladu s meta.rqmts subclause standard, kompilátor MSVC nyní vyvolá chybu, když najde uživatelem definovanou specializaci některé ze zadaných type_traits šablon v std oboru názvů. Pokud není uvedeno jinak, takové specializace vedou k nedefinovanému chování. Následující příklad má nedefinované chování, protože porušuje pravidlo a static_assert selhání s chybou C2338.

#include <type_traits>
struct S;

template<>
struct std::is_fundamental<S> : std::true_type {};

static_assert(std::is_fundamental<S>::value, "fail");

Chcete-li se vyhnout chybě, definujte strukturu, která dědí z preferovaného type_traitobjektu , a specializovat se, že:

#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");

Změny relačních operátorů poskytovaných kompilátorem

Kompilátor MSVC teď implementuje následující změny relačních operátorů na P1630R1, pokud /std:c++20 je tato možnost /std:c++latest povolená:

Kompilátor už nepřepíše výrazy, které používajíoperator==, pokud zahrnují návratový typ, který není .bool Následující kód nyní vytvoří chybu 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
}

Abyste se této chybě vyhnuli, musíte explicitně definovat potřebný operátor:

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;
}

Kompilátor už nedefinuje výchozí relační operátor, pokud je členem třídy podobné sjednocení. Následující příklad nyní vytvoří chybu 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;
}

Pokud se chcete této chybě vyhnout, definujte tělo operátoru:

#include <compare>

union S {
    int a;
    char b;
    auto operator<=>(const S&) const { ... }
};

bool lt(const S& lhs, const S& rhs) {
    return lhs < rhs;
}

Kompilátor již nebude definovat výchozí relační operátor, pokud třída obsahuje referenční člen. Následující kód teď vytvoří chybu C2120:

#include <compare>

struct U {
    int& a;
    auto operator<=>(const U&) const = default;
};

bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}

Pokud se chcete této chybě vyhnout, definujte tělo operátoru:

#include <compare>

struct U {
    int& a;
    auto operator<=>(const U&) const { ... };
};

bool lt(const U& lhs, const U& rhs) {
    return lhs < rhs;
}

Vylepšení shody v sadě Visual Studio 2019 verze 16.5

Explicitní deklarace specializace bez inicializátoru není definice

V části /permissive-MSVC nyní vynucuje standardní pravidlo, které explicitní deklarace specializace bez inicializátorů nejsou definice. Dříve by deklarace byla považována za definici s výchozí inicializátorem. Účinek je pozorovatelný v době propojení, protože program v závislosti na tomto chování teď může mít nevyřešené symboly. Výsledkem tohoto příkladu je chyba:

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.

Pokud chcete tento problém vyřešit, přidejte inicializátor:

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;
}

Výstup preprocesoru zachovává nové čáry.

Experimentální preprocesor nyní zachovává nové čáry a prázdné znaky při použití /P nebo /E s /experimental:preprocessor.

Vzhledem k tomuto příkladu zdroje,

#define m()
line m(
) line

Předchozí výstup /E :

line line
#line 2

Nový výstup /E je nyní:

line
 line

import a module klíčová slova jsou závislá na kontextu.

Direktivy preprocesoru na P1857R1importmodule mají nová omezení jejich syntaxe. Tento příklad se už nekompiluje:

import // Invalid
m;     // error C2146: syntax error: missing ';' before identifier 'm'

Pokud chcete tento problém vyřešit, ponechte import na stejném řádku:

import m; // OK

std::weak_equality Odstranění a odstraněnístd::strong_equality

Sloučení P1959R0 vyžaduje, aby kompilátor odebral chování a odkazy na typy std::weak_equality a std::strong_equality chování.

Kód v tomto příkladu se už nezkompiluje:

#include <compare>

struct S {
    std::strong_equality operator<=>(const S&) const = default;
};

void f() {
    nullptr<=>nullptr;
    &f <=> &f;
    &S::operator<=> <=> &S::operator<=>;
}

Příklad teď vede k těmto chybám:

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'

Pokud chcete tento problém vyřešit, aktualizujte je tak, aby preferovaly předdefinované relační operátory a nahradily odebrané typy:

#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<=>;
}

Změny ochrany TLS

Dříve nebyly správně inicializovány místní proměnné vláken v knihovnách DLL. Jiné než ve vlákně, které načetlo knihovnu DLL, nebyly inicializovány před prvním použitím ve vláknech, které existovaly před načtením knihovny DLL. Tato vada byla opravena. Proměnné lokálních vláken v takové knihovně DLL se inicializují bezprostředně před jejich prvním použitím na těchto vláknech.

Toto nové chování testování inicializace při použití místních proměnných vláken může být zakázáno pomocí možnosti kompilátoru /Zc:tlsGuards- . Nebo přidáním atributu [[msvc:no_tls_guard]] do konkrétních místních proměnných vlákna.

Lepší diagnostika volání odstraněných funkcí

Náš kompilátor byl více permisivní o volání odstraněných funkcí dříve. Pokud se například volání proběhla v kontextu těla šablony, nediagnostikovali bychom volání. Pokud by navíc existovalo více instancí volání odstraněných funkcí, vydáme jenom jednu diagnostiku. Teď pro každou z nich vydáváme diagnostiku.

Jedním z důsledků nového chování může být malá změna způsobující chybu: Kód, který volal odstraněnou funkci, se nediagnostikoval, pokud by ho nikdy nepotřeboval pro generování kódu. Teď ho předem diagnostikujeme.

Tento příklad ukazuje kód, který teď vytvoří chybu:

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

Pokud chcete tento problém vyřešit, odeberte volání odstraněných funkcí:

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 };

Vylepšení shody v sadě Visual Studio 2019 verze 16.6

Standardní datové proudy knihoven odmítnou vkládání nesprávně zakódovaných typů znaků.

Tradičně, vložení wchar_t do std::ostream, a vložení char16_t nebo char32_t do std::ostream nebo std::wostream, výstupem jeho integrální hodnoty. Vložením ukazatelů na tyto typy znaků se zobrazí hodnota ukazatele. Programátoři nenajdou ani jeden případ intuitivní. Často očekávají, že standardní knihovna místo toho překóduje znakový řetězec nebo řetězec znaku ukončeného hodnotou null a vypíše výsledek.

Návrh C++20 P1423R3 přidá přetížení operátoru vložení odstraněného datového proudu pro tyto kombinace datových proudů a znaků nebo typů ukazatelů znaků. Pod /std:c++20 nebo /std:c++latest, přetížení činí tyto vložení špatně vytvořené, namísto chování v tom, co je pravděpodobné nechtěným způsobem. Kompilátor při nalezení vyvolá chybu C2280. Pokud chcete obnovit staré chování, můžete definovat makro _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX201 "escape hatch". (Návrh rovněž odstraní operátory vkládání datových proudů pro char8_t. Naše standardní knihovna implementovala podobné přetížení při přidání char8_t podpory, takže "nesprávné" chování nebylo nikdy k dispozici pro char8_t.)

Tato ukázka ukazuje chování s touto změnou:

#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';
}

Kód teď vytváří tyto diagnostické zprávy:

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

Účinek starého chování můžete dosáhnout ve všech režimech jazyka převodem typů znaků na unsigned inttypy znaků nebo typu const void*ukazatele na:

#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"
}

Změna návratového std::pow() typu pro std::complex

Implementace pravidel povýšení pro návratový typ šablony std::pow() funkce byla dříve nesprávná. Například dříve pow(complex<float>, int) vráceno complex<float>. Nyní se vrátí správně complex<double>. Oprava byla implementována bezpodmínečně pro všechny režimy standardů v sadě Visual Studio 2019 verze 16.6.

Tato změna může způsobit chyby kompilátoru. Například dříve byste mohli násobit pow(complex<float>, int) číslem float. Vzhledem k tomu complex<T> operator* , že očekává argumenty stejného typu, následující příklad teď generuje chybu kompilátoru 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

Existuje mnoho možných oprav:

  • Změňte typ float násobení na double. Tento argument lze převést přímo na typ complex<double> vrácený pow.

  • Zužte výsledek powcomplex<float> tím, že řeknete complex<float>{pow(ARG, ARG)}. Potom můžete pokračovat vynásobením hodnotou float .

  • Předejte float místo int do pow. Tato operace může být pomalejší.

  • V některých případech se můžete vyhnout pow zcela. Například pow(cf, -1) lze nahradit dělením.

switch upozornění pro jazyk C

Kompilátor v sadě Visual Studio 2019 verze 16.6 a novější implementuje některá předexistující upozornění jazyka C++ pro kód zkompilovaný jako C. Následující upozornění jsou nyní povolená na různých úrovních: C4060, C4061, C4062, C4063, C4064, C4065, C4808 a C4809. Upozornění C4065 a C4060 jsou ve výchozím nastavení v jazyce C zakázána.

Upozornění se aktivují u chybějících case příkazů, nedefinovaných enuma chybných bool příkazů switch (to znamená u příkazů, které obsahují příliš mnoho případů). Příklad:

#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
    }
}

Pokud chcete tento kód opravit, odeberte redundantní default případ:

#include <stdbool.h>

int main() {
    bool b = true;
    switch (b) {
        case true: break;
        case false: break;
    }
}

Nepojmenované třídy v typedef deklaracích

V sadě Visual Studio 2019 verze 16.6 a novějších verzích bylo chování typedef deklarací omezeno tak , aby odpovídalo P1766R1. V této aktualizaci nemohou bez názvu třídy v deklaraci typedef mít žádné jiné členy než:

  • nestatické datové členy bez výchozích inicializátorů členů,
  • třídy členů, nebo
  • výčet členů.

Stejná omezení se aplikují rekurzivně na každou vnořenou třídu. Cílem omezení je zajistit jednoduchost struktur, které mají typedef názvy pro účely propojení. Musí být dostatečně jednoduché, aby před tím, než se kompilátor dostane k typedef názvu pro propojení, nebyly potřeba žádné výpočty propojení.

Tato změna má vliv na všechny režimy standardů kompilátoru. Ve výchozím nastavení (/std:c++14) a /std:c++17 režimech kompilátor vygeneruje upozornění C5208 pro nevyhovující kód. Je-li /permissive- zadán, kompilátor vygeneruje upozornění C5208 jako chybu pod /std:c++14 a vygeneruje chybu C7626 v části /std:c++17. Kompilátor vygeneruje chybu C7626 pro nevyhovující kód, pokud /std:c++20 je zadán nebo /std:c++latest je zadán.

Následující ukázka ukazuje konstrukce, které již nejsou povoleny v nepojmenovaných strukturách. V závislosti na zadaném režimu standardů se vygenerují chyby nebo upozornění C5208 nebo C7626:

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;

Výše uvedený kód lze opravit zadáním názvu nepojmenované třídy:

struct B { };
typedef struct S_ : B {
    void f();
    static int i;
    struct U {
        void f();
    };
    int j = 10;
} S;

Výchozí import argumentu v C++/CLI

Rostoucí počet rozhraní API má výchozí argumenty v .NET Core. Teď tedy podporujeme výchozí import argumentů v C++/CLI. Tato změna může přerušit existující kód, kde je deklarováno více přetížení, jak je znázorněno v tomto příkladu:

public class R {
    public void Func(string s) {}   // overload 1
    public void Func(string s, string s2 = "") {} // overload 2;
}

Při importu této třídy do C++/CLI způsobí volání některého z přetížení chybu:

    (gcnew R)->Func("abc"); // error C2668: 'R::Func' ambiguous call to overloaded function

Kompilátor generuje chybu C2668, protože obě přetížení odpovídají tomuto seznamu argumentů. Ve druhém přetížení se druhý argument vyplní výchozím argumentem. Chcete-li tento problém vyřešit, můžete odstranit redundantní přetížení (1). Nebo použijte úplný seznam argumentů a explicitně zadejte výchozí argumenty.

Vylepšení shody v sadě Visual Studio 2019 verze 16.7

je triviálně kopírovatelná definice

C++20 změnil definici triviálně kopírovatelné. Pokud má třída nestatický datový člen s kvalifikovaným volatile typem, už neznamená, že jakýkoli konstruktor generovaný kompilátorem nebo konstruktorem přesunutí nebo operátor přiřazení kopírování nebo přesunutí není triviální. Standardní výbor C++ tuto změnu použil zpětně jako zprávu o vadách. V MSVC se chování kompilátoru nemění v různých režimech jazyka, například /std:c++14 nebo /std:c++latest.

Tady je příklad nového chování:

#include <type_traits>

struct S
{
    volatile int m;
};

static_assert(std::is_trivially_copyable_v<S>, "Meow!");

Tento kód se nekompiluje ve verzích MSVC před sadou Visual Studio 2019 verze 16.7. K dispozici je výchozí upozornění kompilátoru, které můžete použít k detekci této změny. Pokud výše uvedený kód zkompilujete pomocí cl /W4 /w45220, zobrazí se následující upozornění:

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`

Převody typu ukazatel na člen a řetězcový literál na bool zúžení

Standardní výbor C++ nedávno přijal defect Report P1957R2, který se považuje za T*bool zužující převod. MSVC opravila chybu v jeho implementaci, která dříve diagnostikovala zúžení, ale nediagnostikovala T*bool převod řetězcového literálu na bool řetězcový literál nebo ukazatel na člena boolna .

Následující program je špatně vytvořený v sadě Visual Studio 2019 verze 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
}

Chcete-li tento kód opravit, přidejte explicitní porovnání nebo nullptrvyhněte kontextům, kdy zúžení převodů je špatně tvořeno:

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 se konvertibilní bool pouze jako přímá inicializace.

V jazyce C++11 nullptr je převoditelný bool pouze jako přímý převod, například při inicializaci bool pomocí seznamu složených inicializátorů. Toto omezení nikdy nevynutilo MSVC. MSVC teď implementuje pravidlo v části /permissive-. Implicitní převody jsou nyní diagnostikovány jako špatně vytvořené. Kontextový převod na bool je stále povolen, protože přímá inicializace bool b(nullptr) je platná.

Ve většině případů je možné chybu opravit nahrazením výrazem nullptrfalse, jak je znázorněno v tomto příkladu:

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
}

Odpovídající chování inicializace pole s chybějícími inicializátory

Dříve měl MSVC nevyhovující chování pro inicializace polí, u které chyběly inicializátory. MSVC vždy volal výchozí konstruktor pro každý prvek pole, který neměl inicializátor. Standardní chování je inicializovat každý prvek s prázdným složeným závorkou-initializer-list ({}). Kontext inicializace prázdného závorky-initializer-list je copy-initialization, což neumožňuje volání explicitních konstruktorů. Mohou existovat také rozdíly za běhu, protože použití {} k inicializaci může volat konstruktor, který přebírá std::initializer_list, místo výchozí konstruktor. V části /permissive-.

Tady je příklad změněného chování:

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
}

Inicializace členů třídy s přetíženými názvy je správně sekvencovaná

Zjistili jsme chybu v interní reprezentaci datových členů třídy, když je název typu také přetížen jako název datového člena. Tato chyba způsobila nekonzistence v agregované inicializaci a pořadí inicializace členů. Vygenerovaný inicializační kód je teď správný. Tato změna ale může vést k chybám nebo upozorněním ve zdroji, které neúmyslně závisely na chybně seřazených členech, jako v tomto příkladu:

// 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
};

V předchozích verzích by konstruktor nesprávně inicializoval datový člen Inner před datovým členem v. (Standard C++ vyžaduje inicializační pořadí, které je stejné jako pořadí deklarací členů). Teď, když vygenerovaný kód odpovídá standardu, je seznam member-init-list mimo pořadí. Kompilátor vygeneruje pro tento příklad upozornění. Pokud ho chcete opravit, přeuspořádejte seznam member-initializer-list tak, aby odrážel pořadí deklarace.

Rozlišení přetížení zahrnující celočíselné přetížení a long argumenty

Standard jazyka C++ vyžaduje řazení převodu longint jako standardního převodu. Předchozí kompilátory MSVC nesprávně řadí jako celočíselné povýšení, které řadí vyšší pro rozlišení přetížení. Toto řazení může způsobit úspěšné vyřešení přetížení, pokud by mělo být považováno za nejednoznačné.

Kompilátor teď považuje pořadí v /permissive- režimu správně. Neplatný kód se správně diagnostikuje, jak je znázorněno v tomto příkladu:

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
}

Tento problém můžete vyřešit několika způsoby:

  • Na webu volání změňte typ předaného argumentu na int. Můžete změnit typ proměnné nebo ho přetypovat.

  • Pokud existuje mnoho webů volání, můžete přidat další přetížení, které přebírá long argument. V této funkci přetypujte a předáte argument přetížení int .

Použití nedefinované proměnné s interním propojením

Verze MSVC před sadou Visual Studio 2019 verze 16.7 přijali použití proměnné deklarované extern , že měla interní propojení a nebyla definována. Takové proměnné nelze definovat v žádné jiné jednotce překladu a nelze vytvořit platný program. Kompilátor teď tento případ diagnostikuje v době kompilace. Tato chyba se podobá chybě pro nedefinované statické funkce.

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.
}

Tento program se dříve nesprávně zkompiloval a propojil, ale nyní vygeneruje chybu C7631.

error C7631: 'anonymous-namespace::x': variable with internal linkage declared but not defined

Tyto proměnné musí být definovány ve stejné jednotce překladu, ve které se používají. Můžete například zadat explicitní inicializátor nebo samostatnou definici.

Úplnost typů a převody ukazatelů odvozených na základ

V standardech C++ před C++20 nebyl převod odvozené třídy na základní třídu vyžadovat, aby odvozená třída byla úplným typem třídy. Standardní výbor C++ schválil změnu zpětné zprávy o vadách, která se vztahuje na všechny verze jazyka C++. Tato změna zarovná proces převodu s vlastnostmi typu, například std::is_base_of, které vyžadují, aby odvozená třída je úplný typ třídy.

Tady je příklad:

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;
};

Tato změna chování se vztahuje na všechny jazykové režimy jazyka C++ MSVC, ne pouze /std:c++20 nebo /std:c++latest.

Konzistentnější diagnostikované převody

MSVC vygeneruje upozornění pro zužování převodů v inicializátoru složeného seznamu. Dříve kompilátor nediagnostikoval zužující převody z větších enum základních typů na užší integrální typy. (Kompilátor je nesprávně považoval za integrální povýšení místo převodu). Pokud je zužující převod úmyslný, můžete se vyhnout upozornění pomocí static_cast argumentu inicializátoru. Nebo zvolte větší celočíselný typ cíle.

Tady je příklad použití explicitního static_cast řešení upozornění:

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
}

Vylepšení shody v sadě Visual Studio 2019 verze 16.8

Rozšíření Class rvalue used as lvalue

MSVC má rozšíření, které umožňuje použití rvalue třídy jako lvalue. Rozšíření neprodlouží životnost hodnoty rvalue třídy a může vést k nedefinované chování za běhu. Nyní vynucujeme standardní pravidlo a zakážeme toto rozšíření v části /permissive-. Pokud ho ještě nemůžete použít /permissive- , můžete /we4238 rozšíření explicitně zakázat. Tady je příklad:

// 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
}

Explicitní specializace v rozšíření mimo obor názvů

MSVC měl rozšíření, které umožňovalo explicitní specializaci v oboru názvů bez oboru názvů. Nyní je součástí standardu po vyřešení CWG 727. Existují však rozdíly v chování. Upravili jsme chování kompilátoru tak, aby odpovídalo standardu.

// 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"

Kontrola abstraktních typů tříd

Jazyk C++20 Standard změnil kompilátory procesů, které slouží ke zjištění použití abstraktního typu třídy jako parametru funkce. Konkrétně už se nejedná o chybu SFINAE. Pokud kompilátor dříve zjistil, že specializace šablony funkce by měla jako parametr funkce abstraktní instanci typu třídy, pak by se tato specializace považovala za špatně vytvořenou. Nepřidá se do sady funkčních kandidátských funkcí. V jazyce C++20 se kontrola parametru abstraktního typu třídy nestane, dokud se funkce nevolá. Výsledkem je, že kód použitý ke kompilaci nezpůsobí chybu. Tady je příklad:

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);
}

Dříve se volání, které compare by se pokusilo specializovat šablonu compare funkce pomocí String argumentu šablony pro T. Nepodařilo by se vygenerovat platnou specializaci, protože String je abstraktní třída. Jediný realizovatelný kandidát by byl compare(const Node&, const Node&). V C++20 však kontrola typu abstraktní třídy neproběhá, dokud se funkce nevolá. Takže specializace compare(String, String) se přidá do sady vhodných kandidátů a zvolí se jako nejlepší kandidát, protože převod z const String& na String je lepší pořadí převodu než převod z const String& na const Node&.

V rámci C++20 je jedním z možných oprav pro tento příklad použití konceptů; to znamená, že změňte definici compare na:

template<typename T>
int compare(T x, T y) requires !std::is_abstract_v<T>
{
    return x < y ? -1 : (x > y ? 1 : 0);
}

Pokud koncepty C++ nejsou dostupné, můžete se vrátit k 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);
}

Podpora P0960R3 – povolení inicializace agregací ze seznamu hodnot v závorkách

C++20 P0960R3 přidává podporu pro inicializaci agregace pomocí seznamu inicializátorů se závorkou. Například následující kód je platný v jazyce C++20:

struct S {
    int i;
    int j;
};

S s(1, 2);

Většina této funkce je sčítá, to znamená, že kód se teď zkompiluje, který se předtím nezkompiloval. Nicméně, to změní chování std::is_constructible. V režimu C++17 se to static_assert nezdaří, ale v režimu C++20 proběhne úspěšně:

static_assert(std::is_constructible_v<S, int, int>, "Assertion failed!");

Pokud použijete tuto vlastnost typu pro řízení rozlišení přetížení, může to vést ke změně chování mezi C++17 a C++20.

Řešení přetížení zahrnující šablony funkcí

Dříve kompilátor povolil kompilaci určitého kódu, který /permissive- by neměl kompilovat. Výsledkem byl, že kompilátor volal nesprávnou funkci, která vede ke změně chování modulu runtime:

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);
}

Volání používá g sadu přetížení, která obsahuje dvě funkce, ::f a N::f. Vzhledem k tomu N::f , že je šablona funkce, kompilátor by měl považovat argument funkce za nededukovaný kontext. To znamená, že v tomto případě by volání g mělo selhat, protože kompilátor nemůže odvodit typ parametru Tšablony . Kompilátor bohužel nezahodil skutečnost, že se už rozhodl, že ::f se pro volání funkce dobře shoduje. Místo generování chyby by kompilátor vygeneroval kód pro volání g jako ::f argument.

Vzhledem k tomu, že v mnoha případech používáme ::f jako argument funkce to, co uživatel očekává, vygenerujeme chybu pouze v případě, že je kód zkompilován /permissive-.

Migrace z /await korutin C++20

Standardní korutiny C++20 jsou teď ve výchozím nastavení zapnuté pod /std:c++20 a /std:c++latest. Liší se od koroutiny TS a podpory v rámci možnosti /await . Migrace ze /await standardních korutin může vyžadovat určité změny zdroje.

Nestandardní klíčová slova

V režimu C++20 nejsou podporovaná stará await klíčová slova a yield klíčová slova. Kód musí používat co_await a co_yield místo toho. Standardní režim také neumožňuje použití return korutiny. Každý return v korutině musí používat co_return.

// /await
task f_legacy() {
    ...
    await g();
    return n;
}
// /std:c++latest
task f() {
    ...
    co_await g();
    co_return n;
}

Typy initial_suspend/final_suspend

V rámci /await, slib počáteční a pozastavení funkce mohou být deklarovány jako vrácení bool. Toto chování není standardní. V jazyce C++20 musí tyto funkce vracet typ očekávané třídy, často jeden z trivial awaitable typů: std::suspend_always pokud funkce dříve vrátila , nebo std::suspend_never pokud se vrátila truefalse.

// /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 yield_value

V jazyce C++20 musí funkce příslibu yield_value vrátit očekávaný typ. V /await režimu yield_value byla funkce povolena k vrácení voida vždy by se pozastavila. Takové funkce lze nahradit funkcí, která vrací 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{}; }
};

Funkce zpracování výjimek

/awaitpodporuje typ příslibu bez funkce zpracování výjimek nebo funkce zpracování výjimek s názvem set_exception .std::exception_ptr V jazyce C++20 musí mít typ příslibu funkci s názvem unhandled_exception , která nepřijímá žádné argumenty. Objekt výjimky lze v případě potřeby získat.std::current_exception

// /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(); }
    ...
};

Vyvolané návratové typy koruínů nejsou podporovány.

C++20 nepodporuje korutiny s návratovým typem, který obsahuje typ zástupného symbolu, například auto. Návratové typy koroutin musí být explicitně deklarovány. Tyto /awaitodvozené typy vždy zahrnují experimentální typ a vyžadují zahrnutí hlavičky, která definuje požadovaný typ: jeden z std::experimental::task<T>, std::experimental::generator<T>nebo 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;
};

Návratový typ return_value

Návratový typ funkce příslibu return_value musí být void. V /await režimu může návratový typ být cokoli a bude ignorován. Tato diagnostika může pomoct odhalit drobné chyby, například když autor nesprávně předpokládá, že se volajícímu vrátí návratová return_value hodnota.

// /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
};

Chování při převodu vrácených objektů

Pokud deklarovaný návratový typ korutinu neodpovídá návratového typu funkce příslibu get_return_object , objekt vrácený z get_return_object objektu se převede na návratový typ korutiny. Pod /await, tento převod se provádí brzy, než korutinové tělo má šanci provést. V /std:c++20 nebo /std:c++latest, tento převod se provádí, když je hodnota vrácena volajícímu. Umožňuje korutiny, které nejsou pozastaveny v počátečním bodu pozastavení, aby využívaly objekt vrácený get_return_object uvnitř korutinového těla.

Parametry příslibu Coroutine

V jazyce C++20 se kompilátor pokusí předat korutinové parametry (pokud existuje) konstruktoru typu příslibu. Pokud selže, opakuje se s výchozím konstruktorem. V /await režimu byl použit pouze výchozí konstruktor. Tato změna může vést k rozdílu v chování, pokud má příslib více konstruktorů. Nebo pokud existuje převod z korutinového parametru na typ příslibu.

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- moduly C++20 jsou ve výchozím nastavení zapnuté v části /std:c++20

Podpora modulů C++20 je ve výchozím nastavení zapnutá v části /std:c++20 a /std:c++latest. Další informace o této změně a scénáře, kde module a import jsou podmíněně považovány za klíčová slova, naleznete v tématu Standardní podpora modulů C++20 s MSVC v sadě Visual Studio 2019 verze 16.8.

Předpokladem pro podporu modulů je teď povolené, permissive- když /std:c++20 je zadáno nebo /std:c++latest je zadáno. Další informace najdete na webu /permissive-.

Pro kód, který byl dříve zkompilován /std:c++latest a vyžaduje nevyhovující chování kompilátoru, je možné zadat, /permissive aby se v kompilátoru vypnul režim striktní shody. Možnost kompilátoru se musí zobrazit za /std:c++latest seznamem argumentů příkazového řádku. Výsledkem je ale chyba, /permissive pokud se zjistí využití modulů:

chyba C1214: Moduly kolidují s nestandardním chováním požadovaným prostřednictvím možnosti

Nejběžnějšími hodnotami pro možnost jsou:

Možnost Popis
/Zc:twoPhase- Pro moduly C++20 je vyžadováno dvoufázové vyhledávání názvů a odvozeno ./permissive-
/Zc:hiddenFriend- Standardní pravidla vyhledávání skrytých názvů skrytých přátel jsou vyžadována pro moduly C++20 a odvozené ./permissive-
/Zc:lambda- Standardní zpracování lambda je vyžadováno pro moduly C++20 a je odvozeno podle /std:c++20 režimu nebo novějšího.
/Zc:preprocessor- Pro použití a vytvoření jednotek hlaviček C++20 se vyžaduje odpovídající preprocesor. Pojmenované moduly tuto možnost nevyžadují.

Možnost /experimental:module je stále nutná k použití std.* modulů, které jsou dodávány se sadou Visual Studio, protože ještě nejsou standardizované.

Možnost /experimental:module také znamená /Zc:twoPhase, /Zc:lambda, a /Zc:hiddenFriend. Dříve se kód zkompilovaný pomocí modulů někdy mohl zkompilovat, /Zc:twoPhase- pokud byl modul spotřebován pouze. Toto chování se už nepodporuje.

Vylepšení shody v sadě Visual Studio 2019 verze 16.9

Kopírování dočasné inicializace v přímé inicializaci odkazu

Problém základní pracovní skupiny CWG 2267 vyřešil nekonzistence mezi seznamem inicializátoru s závorkami a seznamem inicializátorů s složenými závorkami. Usnesení harmonizuje dvě formy.

Visual Studio 2019 verze 16.9 implementuje změněné chování ve všech /std režimech kompilátoru. Vzhledem k tomu, že se jedná o potenciálně zásadní změnu zdroje, je podporována pouze v případě, že je kód zkompilován pomocí /permissive-.

Tato ukázka ukazuje změnu chování:

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
}

Charakteristiky destruktoru a potenciálně vytvořené podobjekty

Základní pracovní skupina problém CWG 2336 pokrývá vynechání o implicitních specifikacích výjimek destruktorů ve třídách, které mají virtuální základní třídy. Vynechání znamenalo destruktor v odvozené třídě může mít slabší specifikaci výjimky než základní třída, pokud by byla tato základna abstraktní a měla virtual základ.

Visual Studio 2019 verze 16.9 implementuje změněné chování ve všech /std režimech kompilátoru.

Tato ukázka ukazuje, jak se změnila interpretace:

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);
};

Před touto změnou byl implicitně definovaný destruktor pro B , noexceptprotože se považují pouze potenciálně vytvořené podobjekty. A základní třída V není potenciálně vytvořený podobjekt, protože je virtual základem a B je abstraktní. Základní třída V je však potenciálně vytvořený podobjekt třídy D, a tak D::~D je určena být noexcept(false), což vede k odvozené třídě se slabší specifikací výjimek než jeho základ. Tato interpretace je nebezpečná. Může vést k nesprávnému chování modulu runtime, pokud dojde k vyvolání výjimky z destruktoru třídy odvozené z B.

Při této změně je destruktor také potenciálně vyvolání, pokud má virtuální destruktor a každá virtuální základní třída má potenciálně vyvolání destruktoru.

Podobné typy a odkazové vazby

Základní pracovní skupina problém CWG 2352 se zabývá nekonzistence mezi pravidly vazby odkazů a změnami podobnosti typu. Nekonzistence byla zavedena v dřívějších sestavách vad (například CWG 330). Toto se týká sady Visual Studio 2019 verze 16.0 až 16.8.

Při této změně může kód, který od verze 16.9 sady Visual Studio 2019 verze 16.9 dříve vázal odkaz na dočasný odkaz v sadě Visual Studio 2019 verze 16.0 až 16.8, nyní svázat přímo, když se typy, které se týkají, se liší pouze kvalifikátory cv.

Visual Studio 2019 verze 16.9 implementuje změněné chování ve všech /std režimech kompilátoru. Je to potenciálně zásadní změna zdroje.

Viz Odkazy na typy s neshodovanými kvalifikátory cv pro související změnu.

Tato ukázka ukazuje změněné chování:

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
}

Aktualizace může změnit chování programu, které závisí na zavedené dočasné:

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 a /Zc:twoPhase- změna chování možností

Za normálních okolností možnosti kompilátoru MSVC fungují na principu, že poslední zobrazená chyba vyhrává. Bohužel to nebyl případ s možnostmi a /Zc:twoPhase- možnostmi/Zc:twoPhase. Tyto možnosti byly "rychlé", takže je pozdější možnosti nemohly přepsat. Příklad:

cl /Zc:twoPhase /permissive a.cpp

V tomto případě první /Zc:twoPhase možnost povolí striktní dvoufázové vyhledávání názvů. Druhá možnost je určená k zakázání režimu striktní shody (je to opak ), /permissive-ale nezakázla /Zc:twoPhase.

Visual Studio 2019 verze 16.9 toto chování změní ve všech /std režimech kompilátoru. /Zc:twoPhase a /Zc:twoPhase- už nejsou "lepivé", a novější možnosti je mohou přepsat.

Explicitní specifikátory noexcept-u šablon destruktoru

Kompilátor dříve přijal šablonu destruktoru deklarovanou se specifikací výjimky, která není vyvolána, ale definována bez explicitního specifikátoru noexcept-specifier. Implicitní specifikace výjimky destruktoru závisí na vlastnostech třídy – vlastnosti, které nemusí být známy v okamžiku definice šablony. Standard C++ také vyžaduje toto chování: Pokud je destruktor deklarován bez specifikátoru noexcept-specifier, pak má implicitní specifikaci výjimky a žádná jiná deklarace funkce nemusí mít specifikátor noexcept-specifier.

Visual Studio 2019 verze 16.9 se změní tak, aby odpovídalo chování ve všech /std režimech kompilátoru.

Tato ukázka ukazuje změnu chování kompilátoru:

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 { /* ... */ }

Přepsané výrazy v jazyce C++20

Od verze 16.2 /std:c++latestsady Visual Studio 2019 kompilátor přijal kód podobný tomuto příkladu:

#include <compare>

struct S {
    auto operator<=>(const S&) const = default;
    operator bool() const;
};

bool f(S a, S b) {
    return a < b;
}

Kompilátor by ale nevolal funkci porovnání, kterou by autor mohl očekávat. Výše uvedený kód by měl být přepsán a < b jako (a <=> b) < 0. Místo toho kompilátor použil uživatelem definovanou funkci převodu operator bool() a porovnával bool(a) < bool(b). V sadě Visual Studio 2019 verze 16.9 a novější kompilátor přepíše výraz pomocí očekávaného výrazu operátoru spaceship.

Změna způsobující chybu zdroje

Správné použití převodů na přepsané výrazy má jiný účinek: Kompilátor také správně diagnostikuje nejednoznačnosti při pokusech o přepsání výrazu. Podívejte se na tento příklad:

struct Base {
    bool operator==(const Base&) const;
};

struct Derived : Base {
    Derived();
    Derived(const Base&);
    bool operator==(const Derived& rhs) const;
};

bool b = Base{} == Derived{};

V jazyce C++17 by byl tento kód přijat z důvodu odvozeného převodu na základě základu Derived na pravé straně výrazu. V jazyce C++20 je přidán také syntetizovaný kandidát výrazu: Derived{} == Base{}. Vzhledem k pravidlům ve standardu o tom, která funkce vyhraje na základě převodů, ukazuje se, že volba mezi Base::operator== a Derived::operator== je necidelná. Vzhledem k tomu, že sekvence převodu ve dvou výrazech nejsou lepší nebo horší než ostatní, výsledkem ukázkového kódu je nejednoznačnost.

Pokud chcete nejednoznačnost vyřešit, přidejte nového kandidáta, který nebude podléhat dvěma pořadím převodu:

bool operator==(const Derived&, const Base&);

Změna způsobující chybu za běhu

Vzhledem k tomu, že operátor přepisuje pravidla v jazyce C++20, je možné přetížit rozlišení najít nového kandidáta, který by jinak nenalezl v režimu nižšího jazyka. A nový kandidát může být lepší než starší kandidát. Podívejte se na tento příklad:

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; }
};

V jazyce C++17 je jediným kandidátem na ci == *this jazyk const_iterator::operator==. Je to shoda, protože *this prochází odvozený-základní převod na const_iterator. V jazyce C++20 se přidá další přepsaný kandidát: *this == ci, který vyvolá iterator::operator==. Tento kandidát nevyžaduje žádné převody, takže je lepší než const_iterator::operator==. Problém s novým kandidátem spočívá v tom, že se právě definuje funkce, takže nová sémantika funkce způsobí nekonečně rekurzivní definici iterator::operator==.

Kompilátor implementuje nové upozornění, které vám pomůže s kódem jako v příkladu:

$ 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

Pokud chcete kód opravit, buďte explicitní o tom, jaký převod se má použít:

struct iterator {
  bool operator==(const const_iterator &ci) const { return ci == static_cast<const const_iterator&>(*this); }
};

Vylepšení shody v sadě Visual Studio 2019 verze 16.10

Nesprávné přetížení zvolené pro inicializaci kopírování třídy

Vzhledem k tomuto ukázkovém kódu:

struct A { template <typename T> A(T&&); };
struct B { operator A(); };
struct C : public B{};
void f(A);
f(C{});

Starší verze kompilátoru nesprávně převedou argument z f typu na objekt A pomocí šablony převedeného konstruktoru AC . Standardní jazyk C++ vyžaduje místo toho použití operátoru B::operator A převodu. V sadě Visual Studio 2019 verze 16.10 a novější se chování řešení přetížení změní tak, aby používalo správné přetížení.

Tato změna může také opravit zvolené přetížení v některých dalších situacích:

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)
    {
        // ...
    }
}

Nesprávná analýza literálů s plovoucí desetinou čárkou

V sadě Visual Studio 2019 verze 16.10 a novějších se literály s plovoucí desetinou čárkou parsují na základě jejich skutečného typu. Starší verze kompilátoru vždy parsovaly literál s plovoucí desetinou čárkou, jako by měl typ double a potom převedl výsledek na skutečný typ. Toto chování může vést k nesprávnému zaokrouhlení a zamítnutí platných hodnot:

// 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;

Nesprávný bod deklarace

Starší verze kompilátoru nemohly zkompilovat vlastní referenční kód jako v tomto příkladu:

struct S {
    S(int, const S*);

    int value() const;
};

S s(4, &s);

Kompilátor by proměnnou s deklaroval, dokud neanalyzoval celou deklaraci, včetně argumentů konstruktoru. Vyhledávání v seznamu argumentů konstruktoru s by se nezdařilo. V sadě Visual Studio 2019 verze 16.10 a novějších se teď tento příklad správně zkompiluje.

Tato změna bohužel může narušit stávající kód, například v tomto příkladu:

S s(1, nullptr); // outer s
// ...
{
   S s(s.value(), nullptr); // inner s
}

V dřívějších verzích kompilátoru s vyhledá v argumentech konstruktoru pro "vnitřní" deklaraci s, najde předchozí deklaraci ("vnější" s) a kód se zkompiluje. Počínaje verzí 16.10 kompilátor místo toho generuje upozornění C4700 . Je to proto, že kompilátor teď před parsováním argumentů konstruktoru deklaruje "vnitřní" s . s Vyhledávání tedy najde "vnitřní"s, které ještě nebylo inicializováno.

Explicitně specializovaný člen šablony třídy

Dřívější verze kompilátoru nesprávně označily explicitní specializaci člena šablony třídy, jako inline by byla definována také v primární šabloně. Toto chování znamenalo, že kompilátor někdy odmítne odpovídající kód. V sadě Visual Studio 2019 verze 16.10 a novější už explicitní specializace není implicitně označená jako inline v /permissive- režimu. Podívejte se na tento příklad:

Zdrojový soubor s.h:

// s.h
template<typename T>
struct S {
    int f() { return 1; }
};
template<> int S<int>::f() { return 2; }

Zdrojový soubor s.cpp:

// s.cpp
#include "s.h"

Zdrojový soubor main.cpp:

// main.cpp
#include "s.h"

int main()
{
}

Pokud chcete vyřešit chybu linkeru v předchozím příkladu, přidejte inline explicitně:S<int>::f

template<> inline int S<int>::f() { return 2; }

Mangling názvu vrácených návratových typů

V sadě Visual Studio 2019 verze 16.10 a novějším kompilátor změnil způsob generování manglovaných názvů pro funkce, které vyvolaly návratové typy. Představte si například tyto funkce:

auto f() { return 0; }
auto g() { []{}; return 0; }

Starší verze kompilátoru by vygenerovaly tyto názvy pro linker:

f: ?f@@YAHXZ -> int __cdecl f(void)
g: ?g@@YA@XZ -> __cdecl g(void)

Typ návratu by byl vynechán g z důvodu jiného sémantického chování způsobeného místním lambda v těle funkce. Tato nekonzistence ztěžovala implementaci exportovaných funkcí, které mají vyvolaný návratový typ: Rozhraní modulu vyžaduje informace o tom, jak se tělo funkce zkompilovalo. Potřebuje informace k vytvoření funkce na straně importu, která může správně propojit s definicí.

Kompilátor nyní vynechá návratový typ vyvolané funkce návratového typu. Toto chování je konzistentní s jinými hlavními implementacemi. U šablon funkcí existuje výjimka: tato verze kompilátoru zavádí nové chování mangled-name pro šablony funkcí, které mají vyvolaný návratový typ:

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;

Mangled names for auto and decltype(auto) now appear in the binary, not theduced return type:

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)

Starší verze kompilátoru by zahrnovaly odvozený návratový typ jako součást podpisu. Když kompilátor zahrnul návratový typ do manglovaného názvu, mohlo by to způsobit problémy s linkerem. Některé jinak dobře vytvořené scénáře by byly pro linker nejednoznačné.

Nové chování kompilátoru může způsobit binární zásadní změnu. Podívejte se na tento příklad:

Zdrojový soubor a.cpp:

// a.cpp
auto f() { return 1; }

Zdrojový soubor main.cpp:

// main.cpp
int f();
int main() { f(); }

Ve verzích starších než 16.10 kompilátor vytvořil název, auto f() který vypadal jako int f(), i když jsou séanticky odlišné funkce. To znamená, že by se příklad zkompiloval. Pokud chcete tento problém vyřešit, nespoléhejte na auto původní definici f. Místo toho ho napište jako int f(). Vzhledem k tomu, že funkce, které vyvolaly návratové typy, jsou vždy zkompilovány, minimalizují se důsledky ABI.

Upozornění pro ignorovaný nodiscard atribut

Předchozí verze kompilátoru by bezobslužně ignorovaly určité použití atributu nodiscard . Ignorovali atribut, pokud byl v syntaktické pozici, která nebyla aplikována na funkci nebo třídu, která byla deklarována. Příklad:

static [[nodiscard]] int f() { return 1; }

V sadě Visual Studio 2019 verze 16.10 a novější kompilátor místo toho generuje upozornění C5240 úrovně 4:

a.cpp(1): warning C5240: 'nodiscard': attribute is ignored in this syntactic position

Pokud chcete tento problém vyřešit, přesuňte atribut na správnou syntaktickou pozici:

[[nodiscard]] static int f() { return 1; }

Upozornění pro include direktivy se systémovými názvy hlaviček v modulu Purview

V sadě Visual Studio 2019 verze 16.10 a novějších kompilátor vygeneruje upozornění, které zabrání běžné chybě při vytváření rozhraní modulů. Pokud za příkaz zahrnete hlavičku export module standardní knihovny, kompilátor vygeneruje upozornění C5244. Tady je příklad:

export module m;
#include <vector>

export
void f(std::vector<int>);

Vývojář pravděpodobně neměl v úmyslu m vlastnit obsah <vector>. Kompilátor teď vygeneruje upozornění, které vám pomůže problém najít a opravit:

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

Pokud chcete tento problém vyřešit, přesuňte se #include <vector> před export module m;:

#include <vector>
export module m;

export
void f(std::vector<int>);

Upozornění pro nepoužívané interní funkce propojení

Kompilátor v sadě Visual Studio 2019 verze 16.10 a novější varuje v dalších situacích, kdy byla odstraněna neodkazovaná funkce s interním propojením. Starší verze kompilátoru by vygenerovaly upozornění C4505 pro následující kód:

static void f() // warning C4505: 'f': unreferenced function with internal linkage has been removed
{
}

Kompilátor teď také varuje před neodkazovanými auto funkcemi a neodkazovanými funkcemi v anonymních oborech názvů. Vygeneruje ve výchozím nastavení upozornění C5245 pro obě následující funkce:

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; };
}

Upozornění při elizi složené závorky

V sadě Visual Studio 2019 verze 16.10 a novějších kompilátor upozorní na inicializační seznamy, které nepoužívají složené závorky pro podobjekty. Kompilátor ve výchozím nastavení generuje upozornění C5246 mimo výchozí nastavení.

Tady je příklad:

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

Pokud chcete tento problém vyřešit, zabalte inicializaci podobjektu do složených závorek:

S2 s2{ { 1, 2 }, 3 };

Správně zjistit, jestli const objekt není inicializován

V sadě Visual Studio 2019 verze 16.10 a novější teď kompilátor generuje chybu C2737 při pokusu o definování objektu const , který není plně inicializován:

struct S {
   int i;
   int j = 2;
};

const S s; // error C2737: 's': const object must be initialized

Starší verze kompilátoru povolily kompilaci tohoto kódu, i když S::i nejsou inicializovány.

Pokud chcete tento problém vyřešit, inicializujete všechny členy před vytvořením const instance objektu:

struct S {
   int i = 1;
   int j = 2;
};

Vylepšení shody v sadě Visual Studio 2019 verze 16.11

/std:c++20 Režim kompilátoru

V sadě Visual Studio 2019 verze 16.11 a novější teď kompilátor podporuje režim kompilátoru /std:c++20 . Dříve byly funkce C++20 dostupné jenom v /std:c++latest režimu v sadě Visual Studio 2019. Funkce C++20, které původně vyžadovaly /std:c++latest režim, teď fungují v /std:c++20 režimu nebo novějších v nejnovějších verzích sady Visual Studio.

Viz také

Shoda jazyka Microsoft C/C++