Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A Visual Studio Microsoft C++ (MSVC) buildelési eszközei minden kiadásban megfelelőségi fejlesztéseket és hibajavításokat végeznek. Ez a cikk a fejlesztéseket főverzió, majd verzió szerint sorolja fel. Ha közvetlenül egy adott verzió módosításaira szeretne ugrani, használja a cikkben található alábbi listát.
Ez a dokumentum a Visual Studio 2019 változásait sorolja fel. A Visual Studio 2022 változásairól a Visual Studio 2022 C++ megfelelőségi fejlesztései című témakörben olvashat. A Visual Studio 2017 változásait a Visual Studio 2017 C++ megfelelőségi fejlesztései című témakörben talál. A korábbi megfelelőségi fejlesztések teljes listáját a Visual C++ Újdonságok 2003–2015 című témakörben találja.
Megfelelőségi fejlesztések a Visual Studio 2019 RTW-ben (16.0-s verzió)
A Visual Studio 2019 RTW a Következő megfelelőségi fejlesztéseket, hibajavításokat és viselkedésváltozásokat tartalmazza a Microsoft C++ fordítóban.
Megjegyzés:
A C++20 funkciók csak a /std:c++latest módban voltak elérhetők a Visual Studio 2019-ben, amíg a C++20 implementációt teljesnek nem tekintették. A Visual Studio 2019 16.11-es verziója bevezeti a /std:c++20 fordító módot. Ebben a cikkben az /std:c++latest módot eredetileg igénylő funkciók mostantól a legújabb Visual Studio verziókban /std:c++20 módban vagy későbbi verziókban működnek. Frissítettük a dokumentációt, hogy megemlítsük /std:c++20, annak ellenére, hogy ez a lehetőség nem volt elérhető a funkciók első megjelenésekor.
Továbbfejlesztett modulok támogatása sablonokhoz és hibaészleléshez
A modulok már hivatalosan is a C++20 szabványban vannak. Továbbfejlesztett támogatás lett hozzáadva a Visual Studio 2017 15.9-es verziójához. További információ: Jobb sablontámogatás és hibaészlelés az MSVC 2017 15.9-es verziójával rendelkező C++ modulokban.
Aggregátumtípus módosított specifikációja
Az összesítő típus specifikációja megváltozott a C++20-ban (lásd: Összesítések tiltása felhasználó által deklarált konstruktorokkal). A Visual Studio 2019-ben ( /std:c++latest vagy /std:c++20 a Visual Studio 2019 16.11-es és újabb verziójában) egy felhasználó által deklarált konstruktort tartalmazó osztály (például a deklarált = default konstruktort is = deletebeleértve) nem összesítés. Korábban csak a felhasználó által biztosított konstruktorok zárták ki az osztályokat az összesítésből. Ez a módosítás további korlátozásokat helyez az ilyen típusok inicializálására.
Az alábbi kód hiba nélkül fordítható le a Visual Studio 2017-ben, de C2280 és C2440 hibákat jelez a Visual Studio 2019-ben a következő szakaszokban: /std:c++20 vagy /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
Részleges támogatás a következőhöz: operator <=>
P0515R3 A C++20 bemutatja a <=> háromirányú összehasonlító operátort, más néven "űrhajó-operátort". A Visual Studio 2019 16.0-s verziója /std:c++latest módban részleges támogatást nyújt az operátor számára, mivel hibákat jelez a már nem engedélyezett szintaxissal kapcsolatban. Az alábbi kód például hiba nélkül fordul le a Visual Studio 2017-ben, de több hibát is generál a Visual Studio 2019-ben a /std:c++20 vagy /std:c++latest alatt:
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.
}
A hibák elkerülése érdekében szúrjon be egy szóközt a jogsértő sorba az utolsó szögletes zárójel elé: U<&S::operator<= > u;.
Nem egyező cv-minősítőkkel rendelkező típusokra mutató hivatkozások
Megjegyzés:
Ez a módosítás csak a Visual Studio 2019 16.0-16.8-ás verzióit érinti. A Visual Studio 2019 16.9-es verziójában kezdődött a visszaállítás.
Korábban az MSVC lehetővé tette egy hivatkozás közvetlen kötését egy olyan típusból, amelynél a cv-minősítők nem egyeznek a legfelső szint alatt. Ez a kötés lehetővé teheti a hivatkozás által hivatkozott feltételezett const-adatok módosítását.
A Visual Studio 2019 16.0-16.8-as verzióihoz készült fordító ehelyett létrehoz egy ideiglenes objektumot, ahogy azt az akkori szabvány előírta. Később a standard visszamenőlegesen megváltozott, így a Visual Studio 2017 és korábbi verzióinak korábbi viselkedése helyes volt, és a Visual Studio 2019 16.0-16.8-os verziója helytelen volt. Ennek következtében ezt a módosítást a Visual Studio 2019 16.9-es verziójától kezdve visszaállítottuk.
Lásd a Hasonló típusokat és hivatkozási kötéseket a kapcsolódó változásokról szóló részben.
A Visual Studio 2017-ben például az alábbi kód figyelmeztetések nélkül fordít le. A Visual Studio 2019 16.0 és 16.8 verziókban a fordító figyelmeztetést ad a C4172-ről. A Visual Studio 2019 16.9-es verziójától kezdve a kód ismét figyelmeztetések nélkül fordítja le:
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 túlterhelt függvényből
Az argumentum reinterpret_cast nem egyike azoknak a környezeteknek, amelyekben egy túlterhelt függvény címe engedélyezett. A következő kód hiba nélkül fordítja le a Visual Studio 2017-ben, de a Visual Studio 2019-ben c2440-et eredményez:
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'
}
A hiba elkerülése érdekében használjon engedélyezett leadott elemet ehhez a forgatókönyvhöz:
int f(int);
int f(float);
using fp = int(*)(int);
int main()
{
fp r = static_cast<fp>(&f); // or just &f;
}
Lambda-lezárások
A C++14-ben a lambda zárótípusai nem literálok. A szabály elsődleges következménye, hogy a lambda nem rendelhető hozzá változóhoz constexpr . Az alábbi kód hiba nélkül fordítja le a Visual Studio 2017-ben, de a Visual Studio 2019-ben c2127-et eredményez:
int main()
{
constexpr auto l = [] {}; // C2127 'l': illegal initialization of 'constexpr' entity with a non-constant expression
}
A hiba elkerülése érdekében távolítsa el a constexpr minősítőt, vagy módosítsa a megfelelőségi módot /std:c++17 vagy egy későbbi verzióra.
std::create_directory hibakódok
P1164-et C++20-ből feltétel nélkül implementálták. Ez a módosítás std::create_directory ellenőrzi, hogy a cél már könyvtár volt-e egy hiba esetén. Korábban az összes ERROR_ALREADY_EXISTS típushibát sikerkódokká alakították, de a könyvtár nem jött létre.
operator<<(std::ostream, nullptr_t)
A LWG 2221 szerint, hozzáadták operator<<(std::ostream, nullptr_t) a streamekhez való íráshoznullptr.
További párhuzamos algoritmusok
Új párhuzamos verziók: is_sorted, is_sorted_until, is_partitioned, set_difference, set_intersection, is_heap és is_heap_until.
Javítások az atomi inicializálásban
A P0883 "Az atomi inicializálás javítása" a std::atomic érték-inicializálásra módosítja a benne lévő T-t az alapértelmezett inicializálás helyett. A javítás akkor érhető el, ha a Clang/LLVM-et a Microsoft standard kódtárával használja. Jelenleg le van tiltva a Microsoft C++ fordítóban, a constexpr feldolgozási hiba megkerülésére.
remove_cvref és remove_cvref_t
Implementálta a remove_cvref és remove_cvref_t típustulajdonságokat a P0550-ből. Ezek eltávolítják a referenciakénti jellegzetességet és a konstans-volatilis minősítést egy típusból anélkül, hogy a függvények és tömbök mutatóvá alakulnának (ellentétben std::decay és std::decay_t).
A funkcióteszt makrók
P0941R2 – A funkciótesztelés makrói befejeződnek, és támogatják a következőt __has_cpp_attribute: . A funkciótesztelési makrók minden szabványos módban támogatottak.
Összesítések tiltása felhasználó által deklarált konstruktorokkal
C++20 P1008R1 – a felhasználó által deklarált konstruktorokkal végzett összesítések tiltása befejeződött.
reinterpret_cast
constexpr függvényben
A reinterpret_cast érvénytelen egy constexpr függvényben. A Microsoft C++ fordító korábban csak akkor utasította el reinterpret_cast , ha egy constexpr környezetben használták. A Visual Studio 2019-ben minden nyelvi szabványmódban a fordító helyesen diagnosztizálja a(z) reinterpret_cast egy constexpr függvény definíciójában. A következő kód most létrehozza a C3615-öt:
long long i = 0;
constexpr void f() {
int* a = reinterpret_cast<int*>(i); // C3615: constexpr function 'f' cannot result in a constant expression
}
A hiba elkerülése érdekében távolítsa el a constexpr módosítót a függvénydeklarációból.
Basic_string tartománykonstruktor diagnosztikáinak javítása
A Visual Studio 2019-ben a basic_string tartománykonstruktor már nem tiltja le a fordító diagnosztikai üzeneteket static_cast. Az alábbi kód figyelmeztetések nélkül fordítható le a Visual Studio 2017-ben, annak ellenére, hogy az adatok wchar_t-ról char-re történő átvitele során out inicializálásakor adatvesztés lehetséges.
std::wstring ws = /* . . . */;
std::string out(ws.begin(), ws.end()); // VS2019 C4244: 'argument': conversion from 'wchar_t' to 'const _Elem', possible loss of data.
A Visual Studio 2019 helyesen ad figyelmeztetést a C4244-es kóddal. A figyelmeztetés elkerülése érdekében inicializálhatja az std::string alábbi példában látható módon:
std::wstring ws = L"Hello world";
std::string out;
for (wchar_t ch : ws)
{
out.push_back(static_cast<char>(ch));
}
Helytelen hívások a += és a -= rendszerben a /clr vagy a /ZW alatt most már helyesen észlelhetők.
A Visual Studio 2017-ben egy hiba került be, amely miatt a fordító csendben figyelmen kívül hagyta a hibákat, és nem generált kódot az érvénytelen += és -= hívásokhoz /clr vagy /ZW alatt. Az alábbi kód a Visual Studio 2017-ben hiba nélkül fordít, de a Visual Studio 2019-ben helyesen c2845-ös hibát jelez:
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.
}
A példában szereplő hiba elkerülése érdekében használja az += operátort a ToString() következő módszerrel: s += E::e.ToString();.
Inicializálók soros statikus adattagokhoz
A rendszer most már helyesen észleli az inline és static constexpr inicializálók érvénytelen taghozzáféréseit. Az alábbi példa hiba nélkül fordítható le a Visual Studio 2017-ben, de a Visual Studio 2019-ben /std:c++17 módban vagy később c2248-at eredményez:
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'.
};
A hiba elkerülése érdekében deklarálja a tagot X::c védettként:
struct X
{
protected:
static inline const int c = 1000;
};
C4800 visszaállítva
Az MSVC korábban rendelkezett egy teljesítmény figyelmeztetéssel, C4800, a bool-re történő implicit átalakítás miatt. Túl zajos volt, és nem lehetett elfojtani, ami azt eredményezte, hogy eltávolítsuk a Visual Studio 2017-ben. A Visual Studio 2017 életciklusa során azonban sok visszajelzést kaptunk az általa megoldott hasznos esetekről. A Visual Studio 2019-ben egy gondosan testre szabott C4800-at és a magyarázó C4165-öt hozunk vissza. Mindkét figyelmeztetést könnyen el lehet tiltani: vagy explicit leadással, vagy a megfelelő típus 0-ával összehasonlítva. A C4800 egy alapértelmezés szerint 4. szintű figyelmeztetés, a C4165 pedig alapértelmezés szerint 3. szintű figyelmeztetés. Mindkettő felderíthető a /Wall fordító beállítással.
Az alábbi példa a C4800 és a C4165 oszlopot emeli ki /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?
}
Az előző példában szereplő figyelmeztetések elkerülése érdekében a kódot a következőképpen írhatja:
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
}
A helyi osztálytagfüggvény nem rendelkezik törzsvel
A Visual Studio 2017-ben a C4822 figyelmeztetés csak akkor jelentkezik, ha a fordítóbeállítás /w14822 explicit módon be van állítva. Ez nem jelenik meg /Wall-val. A Visual Studio 2019-ben a C4822 egy alapértelmezés szerint kikapcsolt figyelmeztetés, amely lehetővé teszi, hogy /Wall felfedezhető legyen anélkül, hogy explicit módon kellene /w14822 beállítani.
void example()
{
struct A
{
int boo(); // warning C4822: Local class member function doesn't have a body
};
}
Függvénysablon-testületek, amelyek if constexpr utasításokat tartalmaznak
A Visual Studio 2019-ben a /std:c++20 vagy /std:c++latest alatt az olyan sablonfüggvények, amelyek if constexpr utasításokat tartalmaznak, további, elemezéssel kapcsolatos ellenőrzéseket kapnak. A Visual Studio 2017-ben például az alábbi kód csak akkor állítja elő a C7510-et , ha a /permissive- beállítás be van állítva. A Visual Studio 2019-ben ugyanez a kód akkor is hibákat okoz, ha a /permissive beállítás be van állítva:
// 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";
}
A hiba elkerülése érdekében adja hozzá a typename kulcsszót a következő deklarációhoz a: typename T::Type a;.
A soros assembly kód nem támogatott a lambda kifejezésben
A Microsoft C++ csapata nemrég értesült egy biztonsági problémáról, amelyben egy lambda belsejében alkalmazott inline assembly futásidőben a ebp (visszatérési címet tároló regiszter) sérüléséhez vezethet. Egy rosszindulatú támadó kihasználhatja ezt a forgatókönyvet. A beágyazott assembler csak x86-on támogatott, és a beágyazott assembler és a fordítóprogram többi része közötti interakció gyenge. Tekintettel ezekre a tényekre és a probléma jellegére, a legbiztonságosabb megoldás az volt, hogy letiltottuk a beágyazott assembler kódot a lambda kifejezésekben.
A "vadonban" talált lambdakifejezésben a beágyazott összeszerelő egyetlen használata a visszatérési cím rögzítése volt. Ebben a forgatókönyvben a visszatérési címet az összes platformon egyszerűen rögzítheti egy fordítói beépített funkció használatával _ReturnAddress().
A következő kód c7553-at állít elő a Visual Studio 2017 15.9-ben és újabb verzióiban a Visual Studio-ban:
#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;
}
A hiba elkerülése érdekében helyezze át a szerelvénykódot egy elnevezett függvénybe az alábbi példában látható módon:
#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());
}
Iterátor hibakeresése és std::move_iterator
Az iterátor hibakeresési funkciót megtanítottuk a std::move_iterator megfelelő kibontására. Például std::copy(std::move_iterator<std::vector<int>::iterator>, std::move_iterator<std::vector<int>::iterator>, int*) most már használhatja a memcpy gyors útvonalat.
Javítások az <xkeycheck.h> kulcsszóérvényesítéshez
Kijavítottuk a kulcsszót lecserélő makrók xkeycheck.h< szabványkódtárbeli kényszerítését>. A kódtár mostantól általános üzenet helyett a ténylegesen észlelt problémás kulcsszót bocsátja ki. C++20 kulcsszavakat is támogat, és elkerüli, hogy az IntelliSense véletlenszerű kulcsszavakat makrókként kezeljen.
Az elosztótípusok már nincsenek elavult állapotban.
std::allocator<void>, std::allocator::size_typeés std::allocator::difference_type a továbbiakban nem lesz elavult.
A sztringkonvertálások szűkítésére vonatkozó figyelmeztetés helyesbítése
Eltávolítottunk egy felesleges static_cast elemet a std::string elemből, amelyet a szabvány nem írt elő, és amely véletlenül a C4244 szűkítési figyelmeztetések elnyomásához vezetett. A(z) std::string::string(const wchar_t*, const wchar_t*) hívási kísérletek most már megfelelően kibocsátják a C4244 figyelmeztetést, amikor egy wchar_t szűkítése történik egy char típusba.
A fájlrendszer< helyességének különböző javításai >
- Kijavítottuk
std::filesystem::last_write_timea hibát a címtár utolsó írási idejének módosításakor. - A
std::filesystem::directory_entrykonstruktor most egy sikertelen eredményt tárol ahelyett, hogy kivételt ad meg, ha nem létező célútvonalat adott meg. - A
std::filesystem::create_directorykétparaméteres verzió az 1-paraméteres verzió meghívására lett módosítva, mivel a mögöttesCreateDirectoryExWfüggvény acopy_symlinkhasználatával dolgozna, amikor aexisting_pegy szimlink. -
std::filesystem::directory_iteratortöbbé nem fog meghiúsulni, ha hibás szimlinket talál. -
std::filesystem::spacemostantól elfogadja a relatív elérési utakat. -
std::filesystem::path::lexically_relativemár nem téveszti össze a perjeleket, amint azt a LWG 3096-ban jelentették. - A
CreateSymbolicLinkWelemet úgy módosítottuk, hogy elfogadja az elérési útvonalakat előrébb mutató perjelekkel astd::filesystem::create_symlink-ben. - A Windows 10 LTSB 1609-ben létező POSIX törlési módú
deletefüggvény körül dolgozott, de valójában nem tudott fájlokat törölni. - A
std::boyer_moore_searchermásoló konstruktorok és astd::boyer_moore_horspool_searchermásolási hozzárendelés-operátorok most már ténylegesen másolnak dolgokat.
Párhuzamos algoritmusok Windows 8-on és újabb verziókban
A párhuzamos algoritmusok kódtára mostantól megfelelően használja a valódi WaitOnAddress családot a Windows 8-on és újabb verziókban, ahelyett, hogy mindig a Windows 7-et és a korábbi hamis verziókat használták.
std::system_category::message() Szóköz
std::system_category::message() most levágja a záró szóközt a visszaadott üzenetből.
std::linear_congruential_engine osztás nullával
Kijavítottunk néhány feltételt, amelyek 0-ra való osztást idéznének elő std::linear_congruential_engine .
Javítások az iterátor kibontásában jelentkező problémákra
Néhány iterátor-kinyitó eszköz először a programozó-felhasználó integráció számára került bemutatásra a Visual Studio 2017 15.8 verziójában. Ezt a C++ csapatblog STL-funkcióival és javításokkal foglalkozó cikkében ismertette a VS 2017 15.8-ban. Ez a gép már nem bontja ki a standard könyvtár iterátorokból származó iterátorokat. Például egy olyan felhasználó, aki a std::vector<int>::iterator-ból származik, és megpróbálja testre szabni a viselkedést, mostantól a standard könyvtári algoritmusok meghívásakor a testre szabott viselkedést kapja, nem pedig a mutató viselkedését.
A rendezetlen tárolófüggvény reserve most már ténylegesen N elem számára foglal helyet, az LWG 2156-ban leírtak szerint.
Időkezelés
Korábban az egyidejűségi kódtárnak átadott időértékek túlcsordultak, például
condition_variable::wait_for(seconds::max()). Most már kijavítottuk a túlcsordulások viselkedését, amely egy látszólag véletlenszerű 29 napos ciklus szerint módosult, amikor az alapul szolgáló Win32 API-k által fogadott uint32_t típusú ezredmásodpercek túlcsordultak.A <ctime> fejléc mostantól helyesen deklarálja
timespecéstimespec_geta(z)stdnévtérben, és ugyanakkor deklarálja őket a globális névtérben is.
Tárolók különböző javításai
Számos szabványkönyvtárbeli belső tárolófüggvény módosításra került
privatea jobb IntelliSense-élmény érdekében. További javítások várhatók a tagokprivatemegjelöléséhez az MSVC későbbi kiadásaiban.Kijavítottuk azokat a kivételbiztonsággal kapcsolatos helyességi problémákat, amelyek miatt a csomópontalapú tárolók, mint például
list,mapésunordered_map, sérültek lettek. Egypropagate_on_container_copy_assignmentvagypropagate_on_container_move_assignmentátcsoportosítási művelet során felszabadítjuk a tároló sentinel csomópontját a régi kiosztóval, végrehajtjuk a POCCA/POCMA hozzárendelést a régi kiosztóval, majd megpróbáljuk megszerezni a sentinel csomópontot az új kiosztótól. Ha ez a tárhely-foglalás nem sikerült, akkor a tároló megsérült. Még csak megsemmisíteni sem lehet, mivel a szentinel csomópont tulajdonlása szigorú adatszerkezeti invariáns. Ezt a kódot úgy javítottuk ki, hogy a forrástároló kiosztójának használatával hozzuk létre az új őr csomópontot, mielőtt megsemmisítenénk a meglévő őr csomópontot.A tárolókat úgy rögzítették , hogy mindig másolják/áthelyezik/felcserélik a kiosztókat aszerint
propagate_on_container_copy_assignment,propagate_on_container_move_assignmentéspropagate_on_container_swapmég a deklaráltis_always_equalkiosztók esetében is .Hozzáadta a rvalue típusú tárolókat elfogadó tárolóegyesítési és tagfüggvény kinyerési túlterheléseit. További információ: P0083 "Térképek és készletek összemásolása"
std::basic_istream::read
\r\n = feldolgozása>\n
std::basic_istream::read-t úgy javították, hogy a \r\n-től \n-ig tartó feldolgozás részeként ideiglenesen ne kerüljön írás a megadott puffer részeibe. Ez a változás felad némi teljesítménybeli előnyt, amelyet a Visual Studio 2017 15.8-ban lehetett elérni a 4K-nál nagyobb méretű olvasások esetén. A karakterenkénti három virtuális hívás elkerülésének hatékonyságnövelő fejlesztései azonban továbbra is jelen vannak.
std::bitset Konstruktor
A std::bitset konstruktor többé nem olvassa be az egyeseket és a nullákat fordított sorrendben a nagy bithalmazok esetében.
std::pair::operator= regresszió
Kijavítottunk egy regressziót, amely a hozzárendelési operátorban jelentkezett az LWG 2729 "Missing SFINAE on std::pair::operator=" bevezetésekor std::pair. Most már helyesen fogadja el a konvertálható std::pair típusokat.
Nem dedukált környezetek a következőhöz: add_const_t
Kijavítottunk egy kisebb típusú tulajdonsághibát, amelyben add_const_t és a kapcsolódó függvényeknek nem levezetett környezetnek kell lenniük. Más szóval, add_const_t-nek typename add_const<T>::type álnevének kell lennie, nem pedig const T-nak.
Megfelelőségi fejlesztések a 16.1-ben
char8_t
P0482r6. A C++20 egy új karaktertípust ad hozzá, amely az UTF-8 kódegységek megjelenítésére szolgál.
u8 A C++20-ban a sztringliterálok típusa const char8_t[N] lett, a korábban használt const char[N] helyett. Az N2231-ben a C szabvány esetében hasonló változásokra tettek javaslatot. A visszamenőleges kompatibilitás szervizelésére vonatkozó char8_t javaslatokat a P1423r3 tartalmazza. A Microsoft C++ fordító támogatást nyújt a char8_t számára a Visual Studio 2019 16.1-es verziójában, amikor megadja a /Zc:char8_t fordító opciót. A C++17 viselkedésre a /Zc:char8_t- használatával állítható vissza. Az IntelliSense-t használó EDG-fordító még nem támogatja a Visual Studio 2019 16.1-es verziójában. Olyan hamis IntelliSense-hibákat láthat, amelyek nem befolyásolják a tényleges fordítást.
példa
const char* s = u8"Hello"; // C++17
const char8_t* s = u8"Hello"; // C++20
std::type_identity metafunkció és std::identity függvényobjektum
P0887R1 type_identity. Az elavult std::identity osztálysablon-bővítmény el lett távolítva, és a C++20 std::type_identity metafunkciós és std::identity függvényobjektumra cserélődött. Mindkettő csak a /std:c++latest alatt érhető el (/std:c++20 a Visual Studio 2019 16.11-es és újabb verziójában).
Az alábbi példa a Visual Studio 2017-ben a std::identity C4996 elavulásra figyelmeztető üzenetet jeleníti meg (a <type_traits> definiált).
#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);
Az alábbi példa bemutatja, hogyan használhatja az új std::identity (a <funkcionális> csoportban definiálva) együtt az új std::type_identity-mal:
#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);
Általános lambdák szintaxis-ellenőrzése
Az új lambda processzor lehetővé teszi a konformitási módú szintaktikai ellenőrzések elvégzését az általános lambdákban /std:c++latest alatt (a Visual Studio 2019 16.11-es vagy újabb verzióiban) vagy bármely más nyelvi módban a Visual Studio 2019 16.9-es vagy újabb verziójával /std:c++20 alatt (korábban elérhető volt a Visual Studio 2019 16.3-as verziójában /Zc:lambda néven).
Az örökölt lambda-processzor figyelmeztetések nélkül fordítja le ezt a példát, de az új lambda processzor C2760 hibát okoz:
void f() {
auto a = [](auto arg) {
decltype(arg)::Type t; // C2760 syntax error: unexpected token 'identifier', expected ';'
};
}
Ez a példa a fordító által kikényszerített helyes szintaxist mutatja be:
void f() {
auto a = [](auto arg) {
typename decltype(arg)::Type t;
};
}
Függvényhívások argumentumfüggő keresése
P0846R0 (C++20) Nagyobb lehetőség a függvénysablonok keresésére argumentumfüggő kereséssel a függvényhívási kifejezésekhez explicit sablonargumentumokkal. A /std:c++latest szükséges (vagy a /std:c++20 a Visual Studio 2019 16.11-es vagy újabb verziójához).
Kijelölt inicializálás
P0329R4 (C++20) A kijelölt inicializálás lehetővé teszi, hogy egyes tagok összesített inicializálásban legyenek kiválasztva a Type t { .member = expr } szintaxis használatával. A /std:c++latest szükséges (vagy a /std:c++20 a Visual Studio 2019 16.11-es vagy újabb verziójához).
Az felsorolási típus átalakításának rangsorolása a fix alapértelmezett típusra
A fordító mostantól az N4800 11.3.3.2 pont, az implicit konverziós sorozatok rangsorolása (4.2) szerint rangsorolja az enum konverziókat.
- Az olyan átalakítás, amely átalakít egy felsorolást, amelynek statikusan meghatározott típusa megegyezik az alap típusával, jobb, mint az a átalakítás, amelyet a módosított típussal rendelkező alaptípusra alakítanak, ha a kettő eltérő.
Ez a konverziós rangsor nem lett megfelelően implementálva a Visual Studio 2019 16.1-es verziója előtt. A megfelelő viselkedés megváltoztathatja a túlterhelésfeloldási viselkedést, vagy kétértelműséget okozhat, ha korábban nem észleltek ilyet.
Ez a fordítói viselkedésváltozás minden /std módra vonatkozik, és forráskód és bináris szintű változásokat okoz.
Az alábbi példa bemutatja, hogyan változik a fordító viselkedése a 16.1-s és újabb verziókban:
#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{});
}
Új és frissített standard kódtárfüggvények (C++20)
-
starts_with()ésends_with()számárabasic_stringésbasic_string_view. -
contains()asszociatív tárolók esetében. -
remove(),remove_if(), ésunique()mostlistésforward_listvisszasize_type. -
shift_left()ésshift_right()hozzáadva a <algoritmus>hoz.
Megfelelőségi fejlesztések a 16.2-ben
noexcept
constexpr Funkciók
constexpr függvények nem tekinthetők alapértelmezés szerint noexcept-nek, ha állandó kifejezésben használják őket. Ez a viselkedésváltozás a Core Working Group (CWG) CWG 1351 megoldásából származik, és engedélyezve van a /permissive-. Az alábbi példa a Visual Studio 2019 16.1-es és korábbi verziójában áll össze, de a C2338-at a Visual Studio 2019 16.2-es verziójában állítja elő:
constexpr int f() { return 0; }
int main() {
static_assert(noexcept(f()), "f should be noexcept"); // C2338 in 16.2
}
A hiba kijavításához adja hozzá a kifejezést a noexcept függvénydeklarációhoz:
constexpr int f() noexcept { return 0; }
int main() {
static_assert(noexcept(f()), "f should be noexcept");
}
Bináris kifejezések különböző számtípusokkal
A C++20 elavulttá tette a szokásos aritmetikai átalakításokat az operandusokon, ahol:
Az egyik operandus enumerálási típusú, és
a másik eltérő enumerációs típusú vagy lebegőpontos típusú.
További információ: P1120R0.
A Visual Studio 2019 16.2-es és újabb verzióiban a következő kód 4. szintű C5054 figyelmeztetést ad, ha engedélyezve van a /std:c++latest fordító opció (/std:c++20 a Visual Studio 2019 16.11-es és újabb verzióiban).
enum E1 { a };
enum E2 { b };
int main() {
int i = a | b; // warning C5054: operator '|': deprecated between enumerations of different types
}
Annak elkerülése érdekében, hogy figyelmeztetést kapjon, használja a static_cast-t a második operandus konvertálására.
enum E1 { a };
enum E2 { b };
int main() {
int i = a | static_cast<int>(b);
}
Amikor bináris műveletet használ egy felsorolás és egy lebegőpontos típus között, ez most 1. szintű C5055 figyelmeztetés, ha a /std:c++latest fordító beállítást engedélyezik (/std:c++20 a Visual Studio 2019 16.11-es verziójában és később):
enum E1 { a };
int main() {
double i = a * 1.1;
}
Annak elkerülése érdekében, hogy figyelmeztetést kapjon, használja a static_cast-t a második operandus konvertálására.
enum E1 { a };
int main() {
double i = static_cast<int>(a) * 1.1;
}
Tömbök egyenlősége és relációs összehasonlítása
A C++20-ban (P1120R0) a tömbtípusú operandusok egyenlősége és relációs összehasonlítása le lett értéktelenítve. Más szóval, két tömb összehasonlító művelete (a dimenziók és a méretek hasonlósága ellenére) most figyelmeztetés. A Visual Studio 2019 16.2-es és újabb verzióiban az alábbi kód C5056 szintű figyelmeztetést ad, amikor engedélyezve van a /std:c++latest fordító kapcsoló (a Visual Studio 2019 16.11-es és újabb verzióiban):
int main() {
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3 };
if (a == b) { return 1; } // warning C5056: operator '==': deprecated for array types
}
A figyelmeztetés elkerülése érdekében összehasonlíthatja az első elemek címét:
int main() {
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3 };
if (&a[0] == &b[0]) { return 1; }
}
Annak megállapításához, hogy két tömb tartalma egyenlő-e, használja a függvényt std::equal :
std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
Az űrhajó-operátor meghatározásának hatása a ==!=
Az űrhajó-operátor (<=>) definíciója már nem írja át az űrhajó operátorát (P1185R2) tartalmazó == kifejezéseket, vagy != ha az űrhajó operátora nincs megjelölve = default (P1185R2). Az alábbi példa a Visual Studio 2019 RTW-ben és a 16.1-es verzióban áll össze, de a C2676-ot a Visual Studio 2019 16.2-es verziójában állítja elő:
#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
}
A hiba elkerülése érdekében definiálja operator== vagy deklarálja alapértelmezettként:
#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;
}
Standard könyvtár javításai
-
<charconv>
to_chars()rögzített/tudományos pontossággal. (Az általános pontosságot jelenleg a 16.4-re tervezik.) -
P0020R6:
atomic<float>, ,atomic<double>atomic<long double> - P0463R1: Endian
-
P0482R6: A kódtár támogatása
char8_t -
P0600R1:
[[nodiscard]]Az STL esetében 1. rész -
P0653R2:
to_address() - P0754R2: <verzió>
-
P0771R1:
noexceptÁthelyezési konstruktor eseténstd::function
Asszociatív tárolók konstans-összehasonlítói
Kód a kereséshez és a beszúráshoz set, map, multiset, és multimap esetén a kódméret csökkentése érdekében össze lett vonva. A beszúrási műveletek mostantól a kisebb összehasonlításokat hívják meg egy const összehasonlító funktoron, ugyanúgy, ahogy ezt a keresési műveletek korábban tették. A következő kód a Visual Studio 2019 16.1-es és korábbi verziójában fordít, de a Visual Studio 2019 16.2-es verziójában a C3848-at emeli ki:
#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);
}
A hiba elkerülése érdekében használja az összehasonlító operátort const:
struct Comparer {
bool operator() (K a, K b) const {
return a.a < b.a;
}
};
Megfelelőségi fejlesztések a Visual Studio 2019 16.3-ás verziójában
Streameltávolítási operátorok eltávolítása esetén char*
A mutató-karakterek stream kivonó operátorai el lettek távolítva, és a karaktertömbök kinyerési operátorai váltották fel (a P0487R1 szerint). A WG21 nem tartja biztonságosnak az eltávolított túlterheléseket.
/std:c++20 vagy /std:c++latest módban a következő példa most a C2679 hibát eredményezi:
// 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)
}
A hiba elkerülése érdekében használja a kinyerési operátort egy char[] változóval:
#include <iostream>
#include <iomanip>
int main() {
char x[42];
std::cin >> std::setw(42);
std::cin >> x; // OK
}
Új kulcsszavak requires és concept
Új kulcsszavak lettek requiresconcept hozzáadva a Microsoft C++ fordítóhoz. Ha bármelyiket azonosítóként próbálja használni a /std:c++20 vagy /std:c++latest módban, a fordító a C2059 hibakódot adja vissza a szintaktikai hiba jelzésére.
A konstruktorok nem használhatók típusnevekként
A fordító ebben az esetben már nem tekinti injektált osztályneveknek a konstruktorneveket: ha egy osztálysablon-specializáció aliasa után egy minősített névben jelennek meg. Korábban a konstruktorok típusnévként is használhatóak voltak más entitások deklarálásához. Az alábbi példa most c3646-ot hoz létre:
#include <chrono>
class Foo {
std::chrono::milliseconds::duration TotalDuration{}; // C3646: 'TotalDuration': unknown override specifier
};
A hiba elkerülése érdekében deklarálja TotalDuration az itt látható módon:
#include <chrono>
class Foo {
std::chrono::milliseconds TotalDuration {};
};
A függvények extern "C" szigorúbb ellenőrzése
Ha egy extern "C" függvényt különböző névterekben deklaráltak, a Microsoft C++ fordító korábbi verziói nem ellenőrizték, hogy a deklarációk kompatibilisek-e. A Visual Studio 2019 16.3-s és újabb verzióiban a fordító ellenőrzi a kompatibilitást. Módban /permissive- a következő kód C2371 és C2733 hibákat eredményez:
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
Az előző példában szereplő hibák elkerülése érdekében használja a bool helyett BOOL-et, és ezt tegye konzisztensen mindkét f deklarációban.
Standard könyvtár javításai
A nem szabványos <stdexcpt.h> és <typeinfo.h> fejlécek el lettek távolítva. A kódnak ehelyett tartalmaznia kell a standard fejléceket: <exception> és <typeinfo>.
Megfelelőségi fejlesztések a Visual Studio 2019 16.4-es verziójában
A minősített azonosítók kétfázisú névkeresésének hatékonyabb érvényesítése a következő helyen: /permissive-
A kétfázisú névkereséshez a sablontestekben használt nem függő neveknek a definíció időpontjában láthatónak kell lenniük a sablon számára. Korábban előfordulhatott, hogy ilyen neveket találtak a sablon példányosításakor. Ez a módosítás megkönnyíti a hordozható és megfelelő kód írását az MSVC-ben a /permissive- jelölő alatt.
A Visual Studio 2019 16.4-es verziójában, amikor a /permissive- zászló be van állítva, a következő példa hibát eredményez, mert N::f sablon definiálásakor f<T> nem látható.
template <class T>
int f() {
return N::f() + T{}; // error C2039: 'f': is not a member of 'N'
}
namespace N {
int f() { return 42; }
}
Ez a hiba általában a hiányzó fejlécek vagy előre deklaráló függvények vagy változók beírásával javítható, ahogyan az alábbi példában látható:
namespace N {
int f();
}
template <class T>
int f() {
return N::f() + T{};
}
namespace N {
int f() { return 42; }
}
Integrál állandó kifejezések implicit konvertálása null mutatóra
Az MSVC fordító most a CWG 903-at konformitási módban (/permissive-) implementálja. Ez a szabály nem engedélyezi az integrál konstanskifejezések implicit konvertálását (kivéve a "0" egész számkonstanst) nullmutató-állandókra. Az alábbi példa c2440-et állít elő megfelelőségi módban:
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 *'
}
A hiba kijavításához használja nullptr a következő helyett: false. A literál 0 továbbra is engedélyezett:
int* f(bool* p) {
p = nullptr; // OK
p = 0; // OK
return nullptr; // OK
}
Általános szabályok egész számok literáltípusaihoz
Konformitás módban (engedélyezve /permissive-) az MSVC az egész szám literáljainak típusaira vonatkozó szabványos szabályokat használja. A decimális literálok túl nagyok voltak ahhoz, hogy elférjenek egy signed int típusban, ezért korábban unsigned int típust kaptak. Most az ilyen literálok a következő legnagyobb signed egész számtípust kapják. long long Emellett az 'll' utótagú literálok, amelyek túl nagyok ahhoz, hogy elférjenek egy signed típusban, típust unsigned long longkapnak.
Ez a változás különböző figyelmeztető diagnosztikát eredményezhet, valamint a literálokon végzett aritmetikai műveletek viselkedésbeli eltéréseit.
Az alábbi példa a Visual Studio 2019 16.4-es verziójának új viselkedését mutatja be. A i változó most már a(z) unsigned int típusú, ezért a figyelmeztetés megjelenik. A változó j magasrendű bitjei 0-ra vannak állítva.
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
}
Az alábbi példa bemutatja, hogyan őrizheti meg a régi viselkedést, és hogyan kerülheti el a figyelmeztetéseket és a futásidejű viselkedés változását:
void f(int r) {
int i = 2964557531u; // OK
long long j = (long long)0x8000000000000000ll >> r; // shift will keep high-order bits
}
Az árnyéksablon paramétereit tartalmazó függvényparaméterek
Az MSVC fordító most hibát jelez, ha egy függvényparaméter árnyékolást ad egy sablonparaméternek:
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);
}
A hiba kijavításához módosítsa az egyik paraméter nevét:
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);
}
A típustulajdonságok felhasználó által biztosított specializációi
A Standard meta.rqmts alklámjának megfelelően az MSVC fordító most hibát jelez, amikor a névtérben type_traits található egyik megadott std sablon felhasználó által definiált specializációját találja. Ha másként nincs megadva, az ilyen specializációk nem definiált viselkedést eredményeznek. Az alábbi példa nem definiált viselkedést mutat, mert megsérti a szabályt, és a static_assert C2338 hibával meghiúsul.
#include <type_traits>
struct S;
template<>
struct std::is_fundamental<S> : std::true_type {};
static_assert(std::is_fundamental<S>::value, "fail");
A hiba elkerülése érdekében definiáljon egy, az előnyben részesítetttőltype_trait öröklő szerkezetet, és erre specializálódjon:
#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");
A fordító által biztosított összehasonlító operátorok változásai
Az MSVC fordító mostantól a következő módosításokat hajtja végre az összehasonlító operátorokon a P1630R1 szerint, ha a /std:c++20 vagy a /std:c++latest beállítás van engedélyezve.
A fordító a továbbiakban nem írja át a kifejezéseket, amelyek operator==-t tartalmaznak, ha olyan visszatérési típust tartalmaznak, amely nem bool. A következő kód most a C2088 hibát eredményezi:
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
}
A hiba elkerülése érdekében explicit módon meg kell határoznia a szükséges operátort:
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;
}
A fordító már nem definiál alapértelmezett összehasonlító operátort, ha egy unióhoz hasonló osztály tagja. Az alábbi példa most c2120-ás hibát eredményez:
#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;
}
A hiba elkerülése érdekében adjon meg egy törzset az operátor számára:
#include <compare>
union S {
int a;
char b;
auto operator<=>(const S&) const { ... }
};
bool lt(const S& lhs, const S& rhs) {
return lhs < rhs;
}
A fordító a továbbiakban nem definiál alapértelmezett összehasonlító operátort, ha az osztály tartalmaz egy referenciatagot. A következő kód most c2120-ás hibát eredményez:
#include <compare>
struct U {
int& a;
auto operator<=>(const U&) const = default;
};
bool lt(const U& lhs, const U& rhs) {
return lhs < rhs;
}
A hiba elkerülése érdekében adjon meg egy törzset az operátor számára:
#include <compare>
struct U {
int& a;
auto operator<=>(const U&) const { ... };
};
bool lt(const U& lhs, const U& rhs) {
return lhs < rhs;
}
Megfelelőségi fejlesztések a Visual Studio 2019 16.5-ös verziójában
Az inicializáló nélküli explicit specializációs deklaráció nem definíció
A területen /permissive-az MSVC egy szabványos szabályt kényszerít ki, amely szerint az inicializálók nélküli explicit specializációs deklarációk nem definíciók. Korábban a deklaráció alapértelmezett inicializálóval rendelkező definíciónak minősül. A hatás a kapcsolat időpontjában figyelhető meg, mivel az e viselkedéstől függő programokban már feloldatlan szimbólumok is lehetnek. Ez a példa most egy hibát eredményez:
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.
A probléma megoldásához adjon hozzá egy inicializálót.
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;
}
Az előfeldolgozó kimenet megőrzi az új sorokat
A kísérleti előfeldolgozó mostantól megőrzi a sortöréseket és a szóközöket, amikor a /P vagy /E együtt használják a /experimental:preprocessor-vel.
A példaforrást tekintve:
#define m()
line m(
) line
Az előző kimenet a /E következő volt:
line line
#line 2
Az új kimenet /E most a következő:
line
line
import és module kulcsszavak környezetfüggőek
A P1857R1 és importmodule az előfeldolgozási irányelvek szintaxisa új korlátozásokkal rendelkezik. Ez a példa már nem fordítja le a következőt:
import // Invalid
m; // error C2146: syntax error: missing ';' before identifier 'm'
A probléma megoldásához tartsa az importálást ugyanazon a sorban:
import m; // OK
std::weak_equality és std::strong_equality eltávolítása
A P1959R0 egyesítéséhez a fordítónak el kell távolítania a viselkedést és a std::weak_equalitystd::strong_equality típusokra mutató hivatkozásokat.
A példában szereplő kód már nem fordítja le a következőt:
#include <compare>
struct S {
std::strong_equality operator<=>(const S&) const = default;
};
void f() {
nullptr<=>nullptr;
&f <=> &f;
&S::operator<=> <=> &S::operator<=>;
}
A példa most a következő hibákhoz vezet:
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'
A probléma megoldásához frissítsen a beépített relációs operátorok előnyben részesítésére, és cserélje le az eltávolított típusokat:
#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<=>;
}
A TLS Guard módosításai
Korábban a DLL-ek szál-helyi változói nem inicializálva lettek megfelelően. A DLL-t betöltő szálon kívül nem inicializálták őket, mielőtt először használták volna a DLL betöltése előtt létező szálakat. Ezt a hibát kijavítottuk. Az ilyen DLL-ekben lévő szál helyi változói közvetlenül az ilyen szálak első használata előtt inicializálva lesznek.
Előfordulhat, hogy a szál-helyi változók használatával végzett inicializálási tesztelés új viselkedése le van tiltva a /Zc:tlsGuards- fordító beállítással. Vagy az attribútum adott szál helyi változóihoz való hozzáadásával [[msvc::no_tls_guard]] .
A törölt függvények hívásainak jobb felismerése
A fordítónk korábban megengedőbb volt a törölt függvények meghívásával kapcsolatban. Ha például a hívások egy sablontörzs kontextusában történnek, nem diagnosztizálnánk a hívást. Emellett, ha a törölt függvények hívásainak több példánya is lenne, csak egy diagnosztikát adnánk ki. Most kibocsátunk egy diagnosztikát mindegyikhez.
Az új viselkedés egyik következménye egy kis törést okozhat: a törölt függvénynek nevezett kód nem lesz diagnosztizálva, ha a kódgeneráláshoz soha nem lenne szükség rá. Most előzetesen diagnosztizáljuk.
Ez a példa egy hibát okozó kódot mutat be:
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
A probléma megoldásához távolítsa el a törölt függvények hívásait:
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 };
Megfelelőségi fejlesztések a Visual Studio 2019 16.6-os verziójában
A standard kódtárfolyamok elutasítják a helytelen kódolt karaktertípusok beszúrását
Hagyományosan, egy wchar_t beszúrása egy std::ostream-be, és egy char16_t vagy char32_t beszúrása egy std::ostream-be vagy std::wostream-be, kiadja annak integrált értékét. Ha mutatókat szúr be ezekbe a karaktertípusokba, az a mutató értékét adja eredményként. A programozók egyik esetet sem találják intuitívnak. Gyakran elvárják, hogy a standard könyvtár átkódolja a karaktert vagy a nullával lezárt karaktersorozatot, és az eredményt adja ki.
A C++20 javaslat P1423R3 hozzáadja a törölt streambeszúrási operátor túlterhelését a stream és a karakter vagy a karaktermutatók ezen kombinációihoz.
/std:c++20 vagy /std:c++latest alatt a túlterhelések miatt ezek a beszúrások rosszul formáltak, ahelyett hogy vélhetően nem szándékos módon viselkednének. A fordító C2280 hibaüzenetet ad, ha ilyen hibát talál. Az „escape hatch” makrót _HAS_STREAM_INSERTION_OPERATORS_DELETED_IN_CXX20 definiálhatja 1 a régi viselkedés visszaállításához. (A javaslat törli a char8_t-hoz a streambeszúrós operátorokat. A standard könyvtár hasonló túlterheléseket valósított meg, amikor a char8_t támogatását hozzáadtuk, így a "helytelen" viselkedés soha nem volt elérhető a char8_t esetében.)
Ez a minta a következő módosítás viselkedését mutatja be:
#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';
}
A kód a következő diagnosztikai üzeneteket hozza létre:
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
Az régi viselkedés hatását az összes nyelvi módban úgy érheti el, hogy a karaktertípusokat unsigned int-ra, vagy a karaktermutató típusokat const void*-re konvertálja.
#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"
}
Megváltozott a std::pow() visszatérési típusa a std::complex esetében
Korábban helytelen volt a függvénysablon std::pow() visszatérési típusára vonatkozó előléptetési szabályok MSVC-implementálása. Például korábban pow(complex<float>, int) visszaadott complex<float>. Most már helyesen adja vissza complex<double>. A javítást feltétel nélkül hajtották végre a Visual Studio 2019 16.6-os verziójában minden szabvány módhoz.
Ez a módosítás fordítóhibákat okozhat. Például korábban meg lehetett szorozni a pow(complex<float>, int)-t egy float-gyel. Mivel complex<T> operator* az azonos típusú argumentumokra számít, a következő példa most c2676-os fordítóhibát bocsát ki:
// 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
Számos lehetséges javítás érhető el:
Módosítsa a szorzás típusát
floata következőredouble: . Ez az argumentum közvetlenül átalakítható acomplex<double>típusra, amelyet apowvisszaad.Szűkítse a(z)
poweredményét úgy, hogy azt mondja:complex<float>complex<float>{pow(ARG, ARG)}. Ezután továbbra is szorozható egyfloatértékkel.Használja
floatahelyett, hogyint-t küldene apow-nak/neki. Ez a művelet lassabb lehet.Bizonyos esetekben teljesen elkerülheti
pow. Példáulpow(cf, -1)osztással helyettesíthető.
switch figyelmeztetések a C-re
A Visual Studio 2019 16.6-os és újabb verzióiban a fordító néhány, C-ként lefordított kódra vonatkozó C++ figyelmeztetést implementál. A következő figyelmeztetések mostantól különböző szinteken engedélyezettek: C4060, C4061, C4062, C4063, C4064, C4065, C4808 és C4809. A C4065 és a C4060 figyelmeztetések alapértelmezés szerint le vannak tiltva A C-ben.
A figyelmeztetések a hiányzó case utasítások, a nem definiált enumés a hibás bool kapcsoló utasítások (vagyis túl sok esetet tartalmazó) esetén aktiválnak. Például:
#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
}
}
A kód kijavításához távolítsa el a redundáns default esetet:
#include <stdbool.h>
int main() {
bool b = true;
switch (b) {
case true: break;
case false: break;
}
}
Meg nem nevezett osztályok a deklarációkban typedef
A Visual Studio 2019 16.6-os és újabb verzióiban a deklarációk viselkedése typedef a P1766R1 való megfelelésre korlátozódott. Ezzel a frissítéssel a deklaráción belüli typedef névtelen osztályoknak nem lehetnek más tagjai, mint:
- nem statikus adattagok alapértelmezett tag inicializálók nélkül,
- tagosztályok, vagy
- tag-enumerálások.
Ugyanezeket a korlátozásokat rekurzív módon alkalmazza a rendszer minden beágyazott osztályra. A korlátozás célja annak biztosítása, hogy az összekapcsolás céljából neveket tartalmazó typedef szerkezetek egyszerűek legyenek. Elég egyszerűnek kell lenniük ahhoz, hogy ne legyen szükség csatolási számításokra, mielőtt a fordító megkapja a typedef csatolás nevét.
Ez a változás a fordító összes szabványmódját érinti. Alapértelmezés szerint (/std:c++14) és /std:c++17 módban a fordító c5208 figyelmeztetést ad ki a nem megfelelő kódra vonatkozóan. Ha meg van adva a /permissive-, a fordító C5208 figyelmeztetést ad ki hibaként a /std:c++14 feltétel alatt, és C7626 hibát ad ki a /std:c++17 esetén. A fordító C7626-os hibát ad ki, ha nem megfelelő kódot adnak meg, amikor /std:c++20 vagy /std:c++latest van megadva.
Az alábbi minta azokat a szerkezeteket mutatja be, amelyek már nem engedélyezettek a névtelen szerkezetekben. A megadott szabványmódtól függően C5208- vagy C7626-hibák vagy figyelmeztetések jelennek meg:
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;
A fenti kód kijavítható úgy, hogy nevet ad a névtelen osztálynak:
struct B { };
typedef struct S_ : B {
void f();
static int i;
struct U {
void f();
};
int j = 10;
} S;
Alapértelmezett argumentum importálása a C++/CLI-ben
Egyre több API rendelkezik alapértelmezett argumentummal a .NET Core-ban. Így mostantól támogatjuk az alapértelmezett argumentumimportálást a C++/CLI-ben. Ez a módosítás megszakíthatja a meglévő kódot, ahol több túlterhelés van deklarálva, ahogy ebben a példában is látható:
public class R {
public void Func(string s) {} // overload 1
public void Func(string s, string s2 = "") {} // overload 2;
}
Ha ezt az osztályt a C++/CLI-be importálja, a túlterhelések egyikének hívása hibát okoz:
(gcnew R)->Func("abc"); // error C2668: 'R::Func' ambiguous call to overloaded function
A fordító C2668 hibát ad ki, mert mindkét túlterhelés megfelel ennek az argumentumlistának. A második túlterhelésben a második argumentumot az alapértelmezett argumentum tölti ki. A probléma megkerüléséhez törölheti a redundáns túlterhelést (1). Vagy használja a teljes argumentumlistát, és explicit módon adja meg az alapértelmezett argumentumokat.
Megfelelőségi fejlesztések a Visual Studio 2019 16.7-es verziójában
triviálisan másolható definíció
A C++20 módosította a definíciót , amely triviálisan másolható. Ha egy osztály nem statikus, minősített típusú adattaggal volatile rendelkezik, az már nem jelenti azt, hogy a fordító által létrehozott másolási vagy áthelyezési konstruktorok, illetve a hozzárendelési operátor másolása vagy áthelyezése nem triviális. A C++ Standard bizottság ezt a módosítást visszamenőlegesen hibajelentésként alkalmazta. Az MSVC-ben a fordító működése nem változik a különböző nyelvi módokon, például /std:c++14 a /std:c++latest.
Íme egy példa az új viselkedésre:
#include <type_traits>
struct S
{
volatile int m;
};
static_assert(std::is_trivially_copyable_v<S>, "Meow!");
Ez a kód nem fordítja le az MSVC verzióit a Visual Studio 2019 16.7-es verziója előtt. Van egy alapértelmezetten kívüli fordítói figyelmeztetés, amellyel észlelheti ezt a változást. Amikor a fenti kódot a cl /W4 /w45220 használatával fordítja le, a következő figyelmeztetés jelenik meg:
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`
A tagmutatókra és szó szerinti karakterláncokra történő konvertálások bool szűkítést jelentenek.
A C++ Standard bizottság nemrégiben elfogadta a Hibajelentés P1957R2, amely szűkülő átalakításnak tekinti T*bool . Az MSVC kijavított egy hibát az implementációjában, amely korábban a T*-t bool-re való szűkítésként diagnosztizálta, de nem diagnosztizálta a sztring literálok bool-re való átalakítását, vagy egy tagra mutató mutató esetén a bool-re való átalakítást.
A Visual Studio 2019 16.7-es verziójában a következő program nem megfelelő:
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
}
A kód kijavításához adjon hozzá explicit összehasonlításokat a nullptrkörnyezetekhez, vagy kerülje azokat a környezeteket, ahol a szűkítési konverziók hibásan alakulnak ki:
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
}
A nullptr_t csak közvetlen inicializáció esetén konvertálható bool.
A C++11-ben nullptr csak bool konvertálható , például amikor bool-t braced initializer-listával inicializál. Ezt a korlátozást az MSVC soha nem hajtotta végre. Az MSVC most implementálja a szabályt a következő alatt /permissive-: . Az implicit konverziók már nem formázottként vannak diagnosztizálva. A kontekstuális átalakítás bool továbbra is engedélyezett, mert a közvetlen inicializálás bool b(nullptr) érvényes.
A hiba a legtöbb esetben a következőre cserélve nullptrfalsejavítható, ahogy az ebben a példában látható:
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
}
A hiányzó inicializálókkal rendelkező tömb inicializálási viselkedésének megfelelő inicializálás
Korábban az MSVC nem megfelelő viselkedést mutatott a tömbök inicializálása során, ha hiányoztak inicializálók. Az MSVC mindig az alapértelmezett konstruktort nevezte minden olyan tömbelemhez, amely nem rendelkezik inicializálóval. A szokásos viselkedés az egyes elemek inicializálása egy üres braced-initializer-listával ({}). Az üres kapcsos inicializálólistához tartozó inicializálási környezet a másolás-inicializálás, amely nem teszi lehetővé az explicit konstruktorok hívásait. Futásidejű különbségek is előfordulhatnak, mivel a {} használata inicializáláskor meghívhatja az alapértelmezett konstruktor helyett egy std::initializer_list-et igénylő konstruktort. A megfelelési viselkedés engedélyezve van a /permissive- alatt.
Íme egy példa a megváltozott viselkedésre:
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
}
A túlterhelt névvel rendelkező osztálytagok inicializálása megfelelően történik
Hibát észleltünk az osztályadat-tagok belső ábrázolásában, amikor egy típusnév is túlterhelt az adattag neveként. Ez a hiba inicializálási és tag-inicializálási sorrendben okozott következetlenségeket. A létrehozott inicializálási kód helyes. Ez a változás azonban olyan hibákhoz vagy figyelmeztetésekhez vezethet a forrásban, amelyek véletlenül a hibásan rendezett tagokra támaszkodtak, ahogyan ebben a példában is látható:
// 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
};
A korábbi verziókban a konstruktor helytelenül inicializálja az adattagot Inner az adattag velőtt. (A C++ szabványhoz olyan inicializálási sorrend szükséges, amely megegyezik a tagok deklarációs sorrendjének sorrendjén. Most, hogy a létrehozott kód a szabványt követi, a tag inicializáló lista rossz sorrendben van. A fordító figyelmeztetést hoz létre ehhez a példához. A hiba kijavításához rendezze újra a tag-initializer-listát a deklarációs sorrendnek megfelelően.
Túlterhelés feloldása integrál túlterhelésekkel és long argumentumokkal
A C++ szabvány megköveteli, hogy a long átalakítást standard átalakításként rangsoroljuk int között. A korábbi MSVC-fordítók helytelenül egész számú előléptetésként rangsorolják, ami magasabb rangsorolással bír a túlterhelés feloldásában. Ez a rangsorolás azt eredményezheti, hogy a túlterhelés feloldása sikeresen megtörténik, amikor egyébként kétértelműnek kellene tekinteni.
A fordító most helyesen veszi figyelembe a sorrendet /permissive- módban. Az érvénytelen kódot megfelelő módon diagnosztizáljuk, mint ebben a példában:
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
}
A problémát többféleképpen is kijavíthatja:
A hívási helyen módosítsa az átadott argumentum típusát a következőre
int: . Módosíthatja a változó típusát, vagy konvertálhatja.Ha sok híváshely van, hozzáadhat egy másik túlterhelést, amely egy
longargumentumot fogad. Ebben a függvényben továbbítja az argumentumot ainttúlterhelésnek.
Nem definiált változó használata belső kapcsolattal
A Visual Studio 2019 16.7-es verziója előtti MSVC-verziók elfogadták a belső kapcsolatokkal rendelkező és nem definiált extern változó használatát. Ezek a változók nem definiálhatók más fordítási egységben, és nem alkothatnak érvényes programot. A fordító most fordításkor diagnosztizálja ezt az esetet. A hiba hasonló a nem definiált statikus függvények hibájához.
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.
}
Ez a program korábban helytelenül lett lefordítva és csatolva, de most a C7631 hibát fogja kibocsátani.
error C7631: 'anonymous-namespace::x': variable with internal linkage declared but not defined
Az ilyen változókat ugyanabban a fordítási egységben kell meghatározni, amelyben használják őket. Megadhat például egy explicit inicializálót vagy egy külön definíciót.
Típus teljessége és származtatott-bázis mutatókonverziók
A C++20 előtti C++ szabványokban a származtatott osztályból alaposztályba való konvertáláshoz nem volt szükség a származtatott osztály teljes osztálytípusára. A C++ szabványbizottság jóváhagyott egy visszamenőleges hibajelentés-módosítást, amely a C++ nyelv minden verziójára vonatkozik. Ez a módosítás a típustulajdonságokhoz igazítja az átalakítás folyamatát, például std::is_base_of, amelyek megkövetelik, hogy a származtatott osztály teljes osztálytípus legyen.
Íme egy példa:
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;
};
Ez a viselkedésváltozás az MSVC összes C++ nyelvi módjára vonatkozik, nem csak a /std:c++20 vagy a /std:c++latest.
A konverziók szűkítését következetesebben diagnosztizálják
Az MSVC figyelmeztetést ad ki a kapcsos lista inicializálójának konverzióinak szűkítése érdekében. Korábban a fordító nem diagnosztizálta a nagyobb enum mögöttes típusok és a szűkebb integráltípusok közötti konverziókat. (A fordító helytelenül az átalakítás helyett integrált előléptetésnek tekintette őket). Ha a szűkítési átalakítás szándékos, a figyelmeztetést elkerülheti az inicializáló argumentum kulcsszavával static_cast. Vagy válasszon egy nagyobb cél integráltípust.
Íme egy példa arra, hogy explicit static_cast módon kezeli a figyelmeztetést:
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
}
Megfelelőségi fejlesztések a Visual Studio 2019 16.8-ás verziójában
„Osztály rérték használata balértékként” kiterjesztés
Az MSVC rendelkezik egy olyan kiterjesztéssel, amely lehetővé teszi, hogy az osztály rvalue-t lvalue-ként használjuk. A bővítmény nem hosszabbítja meg az osztály rvalue élettartamát, és futásidőben meghatározatlan viselkedéshez vezethet. Mostantól érvényesítjük az általános szabályt, és letiltjuk ezt a bővítményt a következő alatt: /permissive-.
Ha még nem tudja használni /permissive- , explicit módon letilthatja /we4238 a bővítményt. Íme egy példa:
// 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
}
A "nem névtér hatókörében alkalmazott explicit specializáció" bővítmény
Az MSVC-nek volt egy bővítménye, amely lehetővé tette a névtéren kívüli hatókör explicit specializálását. Ez most a szabvány része, a CWG 727 feloldása után. Vannak azonban viselkedésbeli különbségek. A fordító viselkedését a standardhoz igazítottuk.
// 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"
Absztrakt osztálytípusok keresése
A C++20 Standard módosította az absztrakt osztálytípus függvényparaméterként való használatát észlelő folyamatfordítókat. Pontosabban ez már nem SFINAE-hiba. Korábban, ha a fordító azt észlelte, hogy egy függvénysablon specializációja absztrakt osztálytípus-példánnyal rendelkezik függvényparaméterként, akkor ez a specializáció hibásan formázottnak minősül. Ez nem lenne hozzáadva az életképes jelölt függvények halmazához. A C++20-ban az absztrakt osztálytípus paraméterének ellenőrzése csak a függvény meghívása után történik meg. Ennek az az oka, hogy a fordításhoz használt kód nem okoz hibát. Íme egy példa:
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);
}
Korábban a compare hívás megpróbálta volna a compare függvénysablont a String sablonargumentum T használatával specializálni. Nem sikerült érvényes specializációt létrehoznia, mert String ez egy absztrakt osztály. Egyetlen életképes jelölt lett volna compare(const Node&, const Node&). A C++20 alatt azonban az absztrakt osztálytípus ellenőrzése csak a függvény meghívása után történik meg. Így a specializáció compare(String, String) hozzáadódik az életképes jelöltek csoportjához, és mint a legjobb jelölt kerül kiválasztásra, mert az átalakulás const String&String-re egy jobb konverziós sorozat, mint az átalakulás const String&const Node&-re.
A C++20 alatt a példa egyik lehetséges megoldása a fogalmak használata; azaz módosítsa a definíciót compare a következőre:
template<typename T>
int compare(T x, T y) requires !std::is_abstract_v<T>
{
return x < y ? -1 : (x > y ? 1 : 0);
}
Vagy ha a C++ fogalmai nem érhetők el, visszatérhet az SFINAE-hez:
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);
}
A P0960R3 támogatása – aggregátumok inicializálásának engedélyezése zárójeles értékek listájából
A C++20 P0960R3 támogatja az összesítés zárójeles inicializálását. Például a következő kód érvényes a C++20-ban:
struct S {
int i;
int j;
};
S s(1, 2);
Ennek a funkciónak a többsége additív, vagyis a kód most már lefordul, amely korábban nem fordult le. Ez azonban megváltoztatja a std::is_constructible viselkedését. C++17 módban ez static_assert nem sikerül, de C++20 módban sikeres:
static_assert(std::is_constructible_v<S, int, int>, "Assertion failed!");
Ha ezt a típustulajdonságt használja a túlterhelés feloldásának szabályozásához, az a C++17 és a C++20 közötti viselkedés megváltozásához vezethet.
Függvénysablonokat tartalmazó túlterhelésfeloldás
Korábban a fordító lehetővé tette, hogy olyan kódokat lefordítson /permissive-, amelyeknek nem szabadna lefordulniuk. Az effektus az volt, hogy a fordító helytelen függvényt hívott meg, ami a futtatókörnyezet viselkedésének változásához vezetett:
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);
}
A hívás g egy túlterhelési készletet használ, amely két függvényt tartalmaz, ::f és N::f. Mivel N::f függvénysablonról van szó, a fordítónak a függvényargumentumot nem dedukált környezetként kell kezelnie. Ez azt jelenti, hogy ebben az esetben a g hívásnak sikertelennek kell lennie, mivel a fordító nem tudja levezetni a sablonparaméter T típusát. Sajnos a fordító nem vetette el azt a tényt, hogy már eldöntötte, hogy a ::f megfelelő egyezés a függvényhíváshoz. Ahelyett hogy hibát adna ki, a fordító olyan kódot generálna, amely meghívja a g-t a ::f argumentumként való használatával.
Mivel sok esetben a ::f használata függvényargumentumként az, amit a felhasználó elvár, csak akkor jelezünk hibát, ha a kód /permissive--vel van lefordítva.
Migrálás a /await C++20 korutinokra
A standard C++20 korutinok mostantól alapértelmezés szerint be vannak kapcsolva /std:c++20 és /std:c++latest alatt. Eltérnek a Coroutines TS-től, valamint a /await opció alatti támogatástól. Az /await-ról standard korutinokra történő áttérés bizonyos forrásmódosításokat igényelhet.
Nem szabványos kulcsszavak
A régi await és yield a kulcsszavak nem támogatottak C++20 módban. A kódnak co_await és co_yield elemeket kell használnia. A standard mód nem teszi lehetővé a return használatát egy koroutinban. A koroutinban minden return-nak/nak használnia kell a co_return-t.
// /await
task f_legacy() {
...
await g();
return n;
}
// /std:c++latest
task f() {
...
co_await g();
co_return n;
}
Az Initial_suspend/final_suspend típusai
A /await esetén az promise initialize és suspend függvényeket a bool visszatérési típussal deklarálhatjuk. Ez a viselkedés nem szabványos. A C++20-ban ezeknek a függvényeknek egy várható osztálytípust kell visszaadnia, amely gyakran a triviális várandós típusok egyike: std::suspend_always ha a függvény korábban visszaadta true, vagy std::suspend_never ha visszaadta false.
// /await
struct promise_type_legacy {
bool initial_suspend() noexcept { return false; }
bool final_suspend() noexcept { return true; }
...
};
// /std:c++latest
struct promise_type {
auto initial_suspend() noexcept { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
...
};
Típus yield_value
C++20-ban a promise függvénynek yield_value egy várakoztatható típust kell visszaadnia.
/await módban a yield_value függvény visszatérhetett void-re, és mindig felfüggesztésre került. Az ilyen függvények helyettesíthetők egy visszaadott std::suspend_alwaysfüggvénnyel.
// /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{}; }
};
Kivételkezelési függvény
/await támogat egy olyan ígérettípust, amely vagy nem rendelkezik kivételkezelő függvénnyel, vagy egy olyan kivételkezelő függvényt, amely egy set_exception paramétert fogad el: std::exception_ptr. A C++20-ban az ígérettípusnak olyan nevű unhandled_exception függvénysel kell rendelkeznie, amely nem vesz fel argumentumokat. A kivétel objektum szükség esetén beszerezhető 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(); }
...
};
A korutinok visszatérési típusainak dedukálása nem támogatott
A C++20 nem támogatja az olyan visszatérési típusú coroutine-t, amely tartalmaz egy helyőrző típust, például auto. A koroutinok visszatérési típusait explicit módon kell deklarálni. A /await alatt ezek a dedukált típusok mindig egy kísérleti típust tartalmaznak, és megkövetelik a szükséges típust definiáló fejléc beillesztését: az egyik std::experimental::task<T>, std::experimental::generator<T> vagy 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;
}
A visszatérési típus return_value
A promise return_value függvény visszatérési típusának void kell lennie. A /await módban a visszatérési típus bármi lehet, és azt figyelmen kívül hagyják. Ez a diagnosztika segíthet a finom hibák észlelésében, például amikor a szerző helytelenül feltételezi, hogy a return_value visszaadott érték visszakerül a hívónak.
// /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
};
Objektumkonvertálási viselkedés visszaadása
Ha a koroutin deklarált visszatérési típusa nem egyezik meg az ígéretfüggvény get_return_object visszatérési típusával, a visszaadott get_return_object objektum a koroutin visszatérési típusára lesz konvertálva. Alatt /await az átalakítás korán történik, még mielőtt a korutin törzse végrehajtásának esélye lenne. A /std:c++20 vagy /std:c++latest esetében az átalakítás akkor történik meg, amikor az értéket visszaadják a hívónak. Lehetővé teszi, hogy a korutinok, amelyek a kezdeti felfüggesztési ponton nem állnak le, kihasználják a korutin törzsében visszaadott get_return_object objektumot.
Coroutine ígéretparaméterek
A C++20-ban a fordító megpróbálja átadni a coroutine paramétereket (ha vannak ilyenek) az ígéret típusú konstruktornak. Ha nem sikerül, alapértelmezett konstruktorral próbálkozik újra. Módban /await csak az alapértelmezett konstruktort használta a rendszer. Ez a változás eltérő viselkedéshez vezethet, ha az ígéret több konstruktort is használ. Vagy ha a koroutin paraméterből az ígéret típusára való átalakítás történik.
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- és a C++20 modulok alapértelmezés szerint be vannak kapcsolva a /std:c++20
A C++20 modulok támogatása alapértelmezés szerint a(z) /std:c++20 és /std:c++latest alatt be van kapcsolva. A változásról és a feltételesen kulcsszavakként kezelt forgatókönyvekről moduleimport a Visual Studio 2019 16.8-as verziójában a Standard C++20 Modulok támogatása az MSVC-vel című témakörben olvashat bővebben.
A modulok támogatásának előfeltételeként mostantól engedélyezett a permissive-, amikor a /std:c++20 vagy a /std:c++latest meg van adva. További információért lásd /permissive-.
A korábban a /std:c++latest alatt lefordított, és nem konform fordítói viselkedést igénylő kód esetében meg lehet adni /permissive-et, hogy a fordítóban kikapcsolja a szigorú megfelelőségi módot. A fordítóopciónak meg kell jelennie /std:c++latest után a parancssori argumentumok listájában.
/permissive A modulhasználat észlelése esetén azonban hiba jelenik meg:
C1214 hiba: A modulok ütköznek az "option" használatával kért nem szabványos viselkedéssel
A beállítás leggyakoribb értékei a következők:
| Lehetőség | Leírás |
|---|---|
/Zc:twoPhase- |
A C++20 modulok esetében kétfázisú névkeresés szükséges, amit a /permissive- is magában foglal. |
/Zc:hiddenFriend- |
A C++20 modulokhoz szabványos rejtett barátnév-keresési szabályok szükségesek, amelyeket a rendszer feltételez /permissive-. |
/Zc:lambda- |
A C++20 modulokhoz szükséges a standard lambda-feldolgozás, amelyet egy /std:c++20 üzemmód vagy későbbi változat is megkíván. |
/Zc:preprocessor- |
A megfelelő előfeldolgozó csak a C++20 fejlécegység használatához és létrehozásához szükséges. A nevesített modulokhoz nincs szükség erre a beállításra. |
A /experimental:module opció továbbra is szükséges a Visual Studióval szállított modulok használatához, mert még nincsenek szabványosítva.
A /experimental:module beállítás azt is jelenti, hogy /Zc:twoPhase, /Zc:lambda és /Zc:hiddenFriend. Korábban a Modulokkal lefordított kódokat néha újra le lehetett fordítani /Zc:twoPhase-, amennyiben a modult csak felhasználták. Ez a viselkedés már nem támogatott.
Megfelelőségi fejlesztések a Visual Studio 2019 16.9-es verziójában
Ideiglenes objektum másolási inicializálása közvetlen referencia-inicializálásban.
A CWG 2267 fő munkacsoportjának problémája a zárójeles inicializálók listája és a kapcsos inicializálók listája közötti ellentmondással foglalkozott. Az állásfoglalás harmonizálja a két űrlapot.
A Visual Studio 2019 16.9-es verziója minden /std fordítómódban implementálja a megváltozott viselkedést. Mivel azonban lehetséges, hogy forrástörő változásról van szó, csak akkor támogatott, ha a kód fordítása a használatával /permissive-történik.
Ez a minta a viselkedés változását mutatja be:
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
}
Destruktorjellemzők és potenciálisan létrehozott alobjektumok
A CWG 2336 alapvető munkacsoport-problémája a virtuális alaposztályokkal rendelkező osztályok destruktorainak implicit kivétel-specifikációira vonatkozó kihagyást ismerteti. A kihagyás azt jelentette, hogy egy származtatott osztály destruktorának gyengébb kivétel-specifikációja lehet, mint az alaposztálynak, ha ez a bázis absztrakt volt, és rendelkezik alapokkal virtual .
A Visual Studio 2019 16.9-es verziója minden /std fordítómódban implementálja a megváltozott viselkedést.
Ez a minta bemutatja, hogyan változott az értelmezés:
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);
};
A módosítás előtt az implicit módon definiált destruktor B az volt noexcept, mert csak a potenciálisan létrehozott alobjektumokat veszi figyelembe. Az alaposztály V nem egy potenciálisan létrehozott alobjektum, mert ez egy virtual alap, és B absztrakt. Az alaposztály V azonban az osztály Dpotenciálisan konfigurálható alobjektuma, és így D::~D határozható noexcept(false)meg, amely egy olyan származtatott osztályhoz vezet, amely az alapnál gyengébb kivételspecifikációval rendelkezik. Ez az értelmezés nem biztonságos. Helytelen futtatókörnyezeti működéshez vezethet, ha kivétel keletkezik egy B osztályból származtatott osztály destruktorából.
Ezzel a változással egy destruktor is dobhat, ha rendelkezik virtuális destruktorral, és bármely virtuális alaposztály potenciálisan dobó destruktorral rendelkezik.
Hasonló típusok és referenciakötés
A CWG 2352 fő munkacsoportjának problémája a referenciakötési szabályok és a típus-hasonlóság változásai közötti ellentmondással foglalkozik. Az inkonzisztencia a korábbi hibajelentésekben (például a CWG 330-ban) jelent meg. Ez hatással volt a Visual Studio 2019 16.0-16.8-ás verziójára.
Ezzel a módosítással a Visual Studio 2019 16.9-es verziójától kezdve az a kód, amely korábban egy ideiglenes változóra kötötte a hivatkozást a Visual Studio 2019 16.0-es és 16.8-es verziói között, mostantól közvetlenül is köthet, ha az érintett típusok csak a cv-minősítőkben különböznek.
A Visual Studio 2019 16.9-es verziója minden /std fordítómódban implementálja a megváltozott viselkedést. Lehetséges, hogy forrástörő változás.
Tekintse meg a változással kapcsolatos nem egyező cv-minősítőkkel rendelkező típusok hivatkozásait.
Ez a minta a megváltozott viselkedést mutatja be:
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
}
A frissítés megváltoztathatja a program viselkedését, amely egy bevezetett ideiglenes megoldásra támaszkodott.
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 és /Zc:twoPhase- opciók viselkedésének megváltoztatása
Az MSVC fordító beállításai általában azon az elven működnek, hogy az utolsó látott nyer. Sajnos nem ez volt a helyzet a /Zc:twoPhase és /Zc:twoPhase- opciókkal. Ezek a beállítások "ragadósak" voltak, ezért a későbbi beállítások nem tudták felülbírálni őket. Például:
cl /Zc:twoPhase /permissive a.cpp
Ebben az esetben az első /Zc:twoPhase lehetőség lehetővé teszi a szigorú kétfázisú névkeresést. A második lehetőség a szigorú megfelelési mód letiltására szolgál (ez az ellentéte /permissive-), de nem tiltja le /Zc:twoPhase.
A Visual Studio 2019 16.9-es verziója ezt a viselkedést minden /std fordítómódban megváltoztatja.
/Zc:twoPhase és /Zc:twoPhase- már nem "ragadós", és a későbbi beállítások felülírhatják őket.
A destruktor sablonok explicit noexcept specifikátorai
A fordító korábban elfogadott egy destruktorsablont, amely nem dobásmentes kivétel specifikációval lett deklarálva, de explicit noexcept specifikátor nélkül van meghatározva. A destruktor implicit kivétel-specifikációja az osztály tulajdonságaitól függ – olyan tulajdonságoktól, amelyek nem feltétlenül ismertek a sablon definíciójakor. A C++ Standard megköveteli ezt a viselkedést is: Ha a deklarátor noexcept-szabó nélkül van deklarálva, akkor implicit kivételspecifikációval rendelkezik, és a függvény más deklarációja nem rendelkezhet noexcept-kijelölővel.
A Visual Studio 2019 16.9-es verziója a megfelelő működésre változik minden /std fordítómódban.
Ez a minta a fordító viselkedésének változását mutatja be:
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 { /* ... */ }
Újraírt kifejezések a C++20-ban
A Visual Studio 2019 16.2-es verziója óta, a /std:c++latest alatt, a fordító elfogadja az alábbi példához hasonló kódot:
#include <compare>
struct S {
auto operator<=>(const S&) const = default;
operator bool() const;
};
bool f(S a, S b) {
return a < b;
}
A fordító azonban nem hívná meg a szerző által várt összehasonlító függvényt. A fenti kódnak át kellett volna írnia a a < b-t (a <=> b) < 0-re. Ehelyett a fordító a felhasználó által definiált operator bool() konverziós függvényt használta, és bool(a) < bool(b)-t összehasonlította. A Visual Studio 2019 16.9-es és újabb verzióiban a fordító újraírja a kifejezést a várt űrhajó operátori kifejezéssel.
Forrástörés módosítása
A konvertálások újraírt kifejezésekre való megfelelő alkalmazása egy másik hatással is jár: a fordító helyesen diagnosztizálja a kifejezés újraírására tett kísérletek kétértelműségét is. Fontolja meg ezt a példát:
struct Base {
bool operator==(const Base&) const;
};
struct Derived : Base {
Derived();
Derived(const Base&);
bool operator==(const Derived& rhs) const;
};
bool b = Base{} == Derived{};
A C++17-ben ezt a kódot a kifejezés jobb oldalán található származtatott alapkonvertálás Derived miatt fogadná el a rendszer. A C++20-ban a szintetizált kifejezésjelölt a következőt is hozzáadja: Derived{} == Base{}. A standardban szereplő szabályok miatt, amelyek alapján a függvény a konverziók alapján nyer, kiderült, hogy a választás a kettő között Base::operator==Derived::operator== nem határozható meg. Mivel a két kifejezés konverziós sorozatai nem jobbak vagy rosszabbak egymásnál, a példakód kétértelműséget eredményez.
A kétértelműség feloldásához adjon hozzá egy új jelöltet, amely nem lesz kitéve a két konverziós sorozatnak:
bool operator==(const Derived&, const Base&);
Futásidejű kompatibilitástörő változás
A C++20 operátor újraírási szabályai miatt lehetséges, hogy a túlterhelés feloldása olyan új jelöltet talál, amelyet egyébként nem találna alacsonyabb nyelvi módban. És az új jelölt jobban illeszkedhet, mint a korábbi jelölt. Fontolja meg ezt a példát:
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; }
};
A C++17-ben az egyetlen jelölt ci == *this a const_iterator::operator==. Ez egyezés, mert *this egy származtatott alapkonvertáláson const_iteratormegy keresztül. A C++20-ban egy másik újraírt jelölt lesz hozzáadva: *this == ci, amely meghívja iterator::operator==. Ez a jelölt nem igényel konverziót, így jobb egyezés, mint const_iterator::operator==. Az új jelölttel az a probléma, hogy ez az éppen definíció alatt álló függvény, ezért a függvény új szemantikája végtelenül rekurzív definíciót okoz iterator::operator==.
A példához hasonló kódban a fordító egy új figyelmeztetést implementál:
$ 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
A kód javításához explicit módon állapítsa meg, hogy melyik átalakítást használja:
struct iterator {
bool operator==(const const_iterator &ci) const { return ci == static_cast<const const_iterator&>(*this); }
};
Megfelelőségi fejlesztések a Visual Studio 2019 16.10-es verziójában
Helytelen túlterhelés van kiválasztva az osztály másolási inicializálásához
A mintakód alapján:
struct A { template <typename T> A(T&&); };
struct B { operator A(); };
struct C : public B{};
void f(A);
f(C{});
A fordító korábbi verziói helytelenül konvertálják a f argumentumát C típusról A típusra a sablonos átalakító konstruktor Ahasználatával. A standard C++ használatához inkább a konverziós operátort B::operator A kell használni. A Visual Studio 2019 16.10-es és újabb verzióiban a túlterhelésfeloldási viselkedés a megfelelő túlterhelés használatára módosul.
Ez a változás más helyzetekben is kijavíthatja a választott túlterhelést:
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)
{
// ...
}
}
Lebegőpontos literálok helytelen elemzése
A Visual Studio 2019 16.10-es és újabb verzióiban a lebegőpontos literálok a tényleges típusuk alapján vannak elemezve. A fordító korábbi verziói mindig úgy elemeztek egy lebegőpontos literált, mintha típus double lenne, majd az eredményt a tényleges típusra konvertálták. Ez a viselkedés az érvényes értékek helytelen kerekítéséhez és elutasításához vezethet:
// 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;
Helytelen deklarációs pont
A fordító korábbi verziói nem tudtak önhivatkozási kódot lefordítani, mint a következő példában:
struct S {
S(int, const S*);
int value() const;
};
S s(4, &s);
A fordító nem deklarálta a változót s , amíg nem elemezte a teljes deklarációt, beleértve a konstruktor argumentumait is. A konstruktor argumentumlistájában a s keresése sikertelen lenne. A Visual Studio 2019 16.10-es és újabb verzióiban ez a példa most már helyesen áll össze.
Ez a módosítás sajnos megszakíthatja a meglévő kódot, mint ebben a példában:
S s(1, nullptr); // outer s
// ...
{
S s(s.value(), nullptr); // inner s
}
A fordító korábbi verzióiban, amikor a s-t keresi a s "belső" deklaráció konstruktorargumentumaiban, megtalálja az előző deklarációt ("külső" s), és a kód lefordul. A 16.10-es verziótól kezdve a fordító a C4700 figyelmeztetést bocsát ki. Ennek az az oka, hogy a fordító most deklarálja a "belsőt" s a konstruktor argumentumainak elemzése előtt. Tehát a s keresés megtalálja a "belső" s, amelyet még nem inicializáltak.
Egy osztálysablon explicit módon specializált tagja
A fordító korábbi verziói helytelenül jelölték meg az osztálysablon tagjainak explicit specializációját úgy, mintha az inline lenne, ha az az elsődleges sablonban is meg volt határozva. Ez a viselkedés azt jelentette, hogy a fordító néha elutasítja a megfelelő kódot. A Visual Studio 2019 16.10-es és újabb verzióiban az explicit specializáció már nincs automatikusan megjelölve inline módban /permissive- módban. Fontolja meg ezt a példát:
Forrásfájl s.h:
// s.h
template<typename T>
struct S {
int f() { return 1; }
};
template<> int S<int>::f() { return 2; }
Forrásfájl s.cpp:
// s.cpp
#include "s.h"
Forrásfájl main.cpp:
// main.cpp
#include "s.h"
int main()
{
}
A fenti példában szereplő hivatkozási hiba elhárításához egyértelműen adja hozzá a(z) inline-t a(z) S<int>::f-hez:
template<> inline int S<int>::f() { return 2; }
A visszatérési típusnév-elágazásból levont érték
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító megváltoztatta a generálás módját az összezavart nevek számára azoknál a függvényeknél, amelyek kikövetkeztetett visszatérési típusokkal rendelkeznek. Vegyük például az alábbi függvényeket:
auto f() { return 0; }
auto g() { []{}; return 0; }
A fordító korábbi verziói a következő neveket generálják a linkerhez:
f: ?f@@YAHXZ -> int __cdecl f(void)
g: ?g@@YA@XZ -> __cdecl g(void)
Meglepő módon a g visszatérési típust kihagyjuk a függvény törzsében lévő helyi lambda okozta egyéb szemantikai viselkedés miatt. Ez az inkonzisztencia megnehezítette az olyan exportált függvények implementálását, amelyeknek a visszatérési típusa levezetett: A modul interfésznek információra van szüksége arról, hogyan lett a függvény törzse lefordítva. Szüksége van az információkra egy olyan függvény létrehozásához az importálási oldalon, amely megfelelően kapcsolódik a definícióhoz.
A fordító most kihagyja a dedukált visszatérési típusú függvény visszatérési típusát. Ez a viselkedés összhangban van más főbb implementációkkal. A függvénysablonok kivételt képeznek: a fordító ezen verziója új összezavart név viselkedést vezet be az olyan függvénysablonokhoz, amelyeknek megállapították a visszatérési típusát.
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;
A torzított nevek a auto és decltype(auto) most már a binárisban jelennek meg, nem a lekövetett visszatérési típusban.
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)
A fordító korábbi verziói tartalmazták a szignatúra részeként a levezetett visszatérési típust. Amikor a fordító belefoglalja a visszatérési típust a módosított névbe, az linker problémákat okozhat. Az egyébként jól formált forgatókönyvek egyes részei kétértelművé válnak az összekötő számára.
Az új fordítóprogram viselkedése bináris kompatibilitási megszakítást okozhat. Fontolja meg ezt a példát:
Forrásfájl a.cpp:
// a.cpp
auto f() { return 1; }
Forrásfájl main.cpp:
// main.cpp
int f();
int main() { f(); }
A 16.10-es verzió előtti verziókban a fordító olyan nevet hozott létre auto f() számára, amely úgy nézett ki, mint int f(), annak ellenére, hogy ezek szemantikailag különböző függvények. Ez azt jelenti, hogy a példa lefordítható. A probléma megoldásához ne támaszkodjon auto az f eredeti definíciójára. Ehelyett írja be a következőképpen int f(): . Mivel a visszacsatolt típusú függvények mindig lefordítva vannak, az ABI-következmények minimálisra csökkennek.
Figyelmeztetés a figyelmen kívül hagyott nodiscard attribútumra
A fordító korábbi verziói csendben figyelmen kívül hagyják az nodiscard attribútumok bizonyos használatát. Figyelmen kívül hagyták az attribútumot, ha olyan szintaktikai helyzetben volt, amely nem vonatkozik a deklarált függvényre vagy osztályra. Például:
static [[nodiscard]] int f() { return 1; }
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító a 4. szintű figyelmeztetést ad ki a C5240 helyett:
a.cpp(1): warning C5240: 'nodiscard': attribute is ignored in this syntactic position
A probléma megoldásához helyezze át az attribútumot a megfelelő szintaktikai pozícióba:
[[nodiscard]] static int f() { return 1; }
Figyelmeztetés a rendszerfejléc-neveket tartalmazó include irányelvekre a modul környezetében
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító figyelmeztetést ad ki, amely megakadályozza a modul felületének gyakori szerzői hibáját. Ha egy export module utasítást követően szabványos könyvtár fejlécet tartalmaz, a fordító a C5244-es figyelmeztetést ad ki. Íme egy példa:
export module m;
#include <vector>
export
void f(std::vector<int>);
A fejlesztő valószínűleg nem akarta, hogy a m modul birtokolja a <vector> tartalmát. A fordító mostantól figyelmeztetést ad ki, amely segít megtalálni és kijavítani a problémát:
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
A probléma megoldásához helyezze #include <vector>export module m; elé.
#include <vector>
export module m;
export
void f(std::vector<int>);
Figyelmeztetés a nem használt belső kapcsolati függvényekre
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító több olyan helyzetben figyelmeztet, amikor a belső kapcsolattal nem rendelkező függvény el lett távolítva. A fordító korábbi verziói c4505 figyelmeztetést adnának ki a következő kódhoz:
static void f() // warning C4505: 'f': unreferenced function with internal linkage has been removed
{
}
A fordító mostantól figyelmeztet a névtelen névterekben nem hivatkozott auto és nem hivatkozott függvényekre is. Alapértelmezés szerint c5245 figyelmeztetést ad ki mindkét alábbi függvényhez:
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; };
}
Figyelmeztetés kapcsos kapcsos elision
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító olyan inicializálási listákra figyelmeztet, amelyek nem használnak zárójeleket az alobjektumokhoz. A fordító nem alapértelmezés szerint adja ki a C5246 figyelmeztetést.
Íme egy példa:
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
A probléma megoldásához csomagolja be az alobjektum inicializálását kapcsos zárójelekbe:
S2 s2{ { 1, 2 }, 3 };
Megfelelő észlelés, ha egy const objektum nincs inicializálva
A Visual Studio 2019 16.10-es és újabb verzióiban a fordító mostantól C2737-es hibát ad ki, amikor megkísérli definiálni a const nem teljesen inicializált objektumot:
struct S {
int i;
int j = 2;
};
const S s; // error C2737: 's': const object must be initialized
A fordító korábbi verziói lehetővé tették a kód fordítását, annak ellenére S::i , hogy nincs inicializálva.
A probléma megoldásához inicializálja az összes tagot, mielőtt létrehoz egy objektumpéldányt const :
struct S {
int i = 1;
int j = 2;
};
Megfelelőségi fejlesztések a Visual Studio 2019 16.11-es verziójában
/std:c++20 fordító mód
A Visual Studio 2019 16.11-es és újabb verzióiban a fordító mostantól támogatja a /std:c++20 fordító módot. Korábban a C++20 jellemzők csak a /std:c++latest módban voltak elérhetők a Visual Studio 2019-ben. A C++20 funkciók, amelyek eredetileg /std:c++latest módot igényeltek, mostantól /std:c++20 módban vagy későbbi verziókban működnek a Visual Studio legújabb verzióiban.