C++23 este standardul așteptat pentru limbajul de programare C++ .
Mai devreme , a făcut o funcție încorporată a compilatorului, sa dovedit a fi eronată [12] . De exemplu: std::is_constant_evaluated()
constexpr size_t strlen ( char const * s ) { //dacă constexpr (std::is_constant_evaluated()) { Era, nu a apelat versiunea asamblatorului if consteval { // A devenit pentru ( const char * p = s ; ; ++ p ) { dacă ( * p == '\0' ) { returnează static_cast < std :: size_t > ( p - s ); } } } altfel { __asm__ ( "Ceva optimizat pentru SSE" ); } }Desigur, compilatorii emit un avertisment, dar nu este evident ce trebuie făcut este corect , altfel versiunea optimizată a asamblatorului nu va rula deloc. if (std::is_constant_evaluated())
Al doilea motiv este interacțiunea dintre și . constexprconsteval
consteval int f ( int i ) { return i ; } constexpr int g ( int i ) { // if (std::is_constant_evaluated()) { Was, not compiled if consteval { // Acum returnează f ( i ) + 1 ; } altfel { întoarcere 42 ; } }Acest cod nu a fost compilat deloc - nu puteți apela o funcție consteval de aici.
Acoladele din partea de atunci sunt necesare, dar în cealaltă parte pot fi omise. Pare imposibil de scris. Vechea funcție nu este interzisă - extrem de rară, dar necesară. if (consteval && n < 0) {
O modalitate ușoară de a obține un obiect ca temporar, cum ar fi [12] :
void pop_front_alike ( Container auto & x ) { std :: erase ( x.begin ( ), x.end ( ) , auto ( x.front ( ) ) ) ; }x.front() - eroare: în funcție de container, această referință se va uita fie la un alt obiect, fie în memoria goală.
Codul de mai jos este corect, dar auditorul poate fi tentat să elimine în mod eronat variabila . a
auto a = x . fata (); std :: erase ( x.begin ( ), x.end ( ) , a ) ;În programarea șablonului, acest tip poate fi dificil de obținut:
folosind T = std :: decay_t < decltype ( x . front ()) > ; std :: erase ( x.begin ( ), x.end ( ) , T ( x.front ( ) ) ) ;Numele a fost abandonat din două motive: prvalue este un concept extrem de tehnic și comportament neadecvat pentru matrice (va da un pointer). prvalue_cast
Metode existente [13] :
matrice ( 1 , 2 , 3 , 4 , 5 ) = 42 ; // arată groaznic matrice [{ 1 , 2 , 3 , 4 , 5 }] = 42 ; // foarte de neînțeles și neplăcut de scris matrice [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ] = 42 ; // un pic mai bine, dar sub capotă e doar înfiorătorPână acum, doar pentru tipurile personalizate [14] .
int buffer [ 2 * 3 * 4 ] = { }; auto s = mdspan < int , extents < 2 , 3 , 4 >> ( buffer ); s [ 1 , 1 , 1 ] = 42 ;Diferite biblioteci implementează sintaxa lipsă în moduri diferite, dar, în orice caz, aceasta nu este compatibilă cu sintaxa matricelor standard și face dificilă găsirea automată a erorilor și inline-ul (implantarea unei funcții direct în codul de apelare).
Subiectul discutiei ramane: este necesar pentru matrice standard; dacă să relaxeze cerințele și să-l permită în afara clasei. operator[]
Una dintre caracteristicile C++ - const-corectness - duce la duplicarea codului sau la metodele de scriere de delegare. Se propune o soluție la aceasta prin șabloane [15]
///// WAS ///// clasa TextBlock { public : char const și operator []( size_t position ) const { // ... returnează text [ poziție ]; } char & operator []( size_t position ) { returnează const_cast < char &> ( static_cast < TextBlock const &> ( aceasta ) [ poziție ] ); } // ... }; ///// DEVENI ///// clasa TextBlock { public : template < typenameSelf > _ auto & operator []( acest Self && self , poziție size_t ) { // ... return self . text [ poziție ]; } // ... };Metodele de extindere nu sunt încă oferite, dar vor fi posibile în viitor.
Lista de indulgențe este lungă și este legată de două lucruri:
Astfel, acum este posibil să scrieți o funcție constexpr care, fără un set de argumente, poate fi executată la compilare [16] .
De asemenea, în funcțiile constexpr sunt permise goto , variabile de tipuri non-literale, variabile statice/intra-thread. Dacă oricare dintre aceste linii este trecută în timpul compilării, funcția este evaluată la execuție. ridicat la 202103L [17] . __cpp_constexpr
Îndepărtează o instrucțiune de mașină dacă clasa nu are date și introducerea eșuează [18] . De exemplu, într-un arbore de auto-echilibrare cu o comandă non-standard (era în C++03 ) și căutare eterogenă ( C++14 ), următorul cod este posibil:
struct CustomCompare { folosind is_transparent = int ; // operator bool static de căutare heterogenă () ( std :: string_view a , std :: string_view b ) // a fost const, a devenit static { return someCustomLess ( a , b ); } }; std :: set < std :: string , CustomCompare > lucruri ;Caracterele din seturile Unicode XID_Start (start) și XID_Continue (altele) sunt acum permise în identificatori .
Identificatorul trebuie normalizat conform algoritmului de „compoziție canonică” (NFC, dezasamblarea caracterelor monolitice în componente și reasamblarea). Dacă nu, programul este incorect.
Această modificare doar face ca suportul Unicode să fie mai consistent, dar nu face nimic pentru a rezolva problemele atacurilor prin șiruri aparent identice [19] . Metodele de transmitere a unor astfel de caractere către linker depind de implementare.
Diferiți compilatori au acționat diferit asupra (facepalm emoji ) pe un wchar_t pe dublu octet (Windows), . Ambele sunt acum interzise [20] . L'\U0001F926'L'ab'
Literele cu mai multe caractere încă funcționează, sunt de tip int. Câte caractere sunt permise și cum vor fi colectate într-un singur număr este determinat de implementare.
Este legal ca unul să fie diferit de celălalt [21] , și este o unitate a unei codări de performanță largi, specifice implementării [22] . wchar_t
UTF-8 ca codificare de traducere multiplatformă trebuie să fie suportat necondiționat de toți compilatorii [23] . Semnul de ordine al octeților este ignorat, cu excepția cazului în care intră în conflict cu steagurile compilatorului. Dacă fișierul este recunoscut ca UTF-8, nu ar trebui să conțină combinații de coduri incorecte - totuși, pot exista combinații corecte corespunzătoare caracterelor care încă nu există.
Înainte era până la implementare, dar s-a dovedit că scopul principal al acestei funcții este de a determina codificarea execuției [24] . De exemplu, codul din SQLite :
/* Verificați dacă aparatul utilizează EBCDIC. (Da, crezi sau nu, mai există mașini care folosesc EBCDIC.) */ #if 'A' == '\301' # define SQLITE_EBCDIC 1 #else # define SQLITE_ASCII 1 #endifToți compilatorii majori funcționează de fapt în acest fel.
Toate cele trei linii sunt rupte în C++20, funcționează din nou în C++23 [25] .
const char * a = u8 "a" ; const char b [] = u8 "b" ; const unsigned char c [] = u8 "c" ;După cum sa dovedit, o astfel de întrerupere a făcut funcțiile constexpr mai complicate și a interferat cu compatibilitatea cu C.
"\u{1F926}"pentru un punct de cod Unicode, pentru octal și pentru hexazecimal [26] . "\o{123}""\x{AB}"
Spărgerea unor astfel de scuturi ( ) este interzisă. "\x{4" "2}"
"\N{LATIN CAPITAL LETTER A WITH MACRON}"vă permite să faceți referire la un simbol prin numele său unicode [27] .
std::functiona devenit una dintre cele mai „grele” părți ale bibliotecii STL. Scăpând de mai multe caracteristici - nu pot fi copiate, câmpuri lipsă și - puteți obține un obiect mult mai ușor [45] . Și, desigur, acest obiect poate funcționa cu cârlige care nu pot fi copiate. targettarget_type
O monada este o caracteristică standard a limbajelor funcționale pentru a efectua o secvență de acțiuni.
În matematică, o secvență de funcții este scrisă ca , ceea ce nu este întotdeauna convenabil - în programare, ceva de genul . x.f().g().h()
std::optional este un înveliș destul de simplu, al cărui sens este de a stoca un obiect sau nimic. Verificările pentru „nimic” ocupă o mare parte a lucrării cu opțional - dar dacă, în procesul de transformare a imaginii, nu există nicio pisică pe ea? Dar dacă nu există loc pentru a trage un arc? [46]
std :: opțional < imagine > get_cute_cat ( const imagine & img ) { return crop_to_cat ( img ) // imagine → opțional; [nullopt] nu există nicio pisică în imagine . and_then ( add_bow_tie ) // imagine → optional; [nullopt] nicăieri pentru a adăuga un arc . and_then ( make_eyes_sparkle ) // imagine → optional; [nullopt] nu poate vedea ochii . transform ( face_mai mic ) // imagine → imagine . transform ( adaugă_curcubeu ); // imagine → imagine }Folosit pentru optimizare extremă la joncțiunea șirurilor și a API-urilor de nivel scăzut:
int compress ( void * out , size_t * out_size , const void * in , size_t in_size ); std :: string CompressWrapper ( std :: string_view input ) { std :: șir comprimat ; comprimat . resize_and_overwrite ( input . size (), [ input ]( char * buf , std :: size_t n ) noexcept { std :: size_t compressed_size = n ; auto is_ok = compress ( buf , & compressed_size , input . data (), input . size ()); assert ( is_ok ); returnează dimensiunea_comprimată ; }); return comprimat ; }Apare întrebarea: ce a fost optimizat în comparație cu cele două ? [13] Faptul este că costul alocării memoriei nu depinde mult de lungimea buffer-ului și, în cele mai multe cazuri, va fi alocată memoriei tampon mult mai mult decât este necesar de fapt pentru șirul comprimat. Noua funcție nu inițializează buffer-ul și repunerea la zero a unei secțiuni foarte lungi de memorie - . resizememset( compressed.data(), compressed.size(), '\0')
A existat un strstream - un flux de date care rulează pe o matrice de lungime limitată. Interzis în C++98, a fost propus un alt mecanism similar.
ieșire char [ 30 ]{}; ospanstream os { span < char > { output }}; os << 10 << 20 << 30 ; auto const sp = os . span (); ASSERT_EQUAL ( 6 , dimensiunea sp . ()); ASSERT_EQUAL ( "102030" , std :: șir ( sp . date (), sp . size ())); ASSERT_EQUAL ( static_cast < void *> ( output ), sp . data ()); // nu se copiază datele ASSERT_EQUAL ( "102030" , output ); // nul-terminated garantatInitial a fost:std::cout << std::format("Hello, {}! You have {} mails", username, email_count);
Aceasta…
Este disponibil unul mai ușor [47] . std::print("Привет, {}! У вас {} писем", username, email_count);
Nume | Bucăți de mantisă | ordine de biți | Notă |
---|---|---|---|
float16_t | 10 + implicit 1 | 5 | Respectă IEEE binary16 |
bfloat16_t | 7 + implicit 1 | opt | Doi octeți superiori IEEE binary32 (≈float), utilizați în bibliotecile AI, de unde și numele brain float |
float32_t | 23 + implicit 1 | opt | Conform IEEE binary32, majoritatea implementărilor float |
float64_t | 52 + implicit 1 | unsprezece | Conform IEEE binary64, majoritatea implementărilor de dublu |
float128_t | 112 + implicit 1 | cincisprezece | Respectă IEEE binary128 |
Funcțiile matematice trebuie să aibă wrappers pentru toate tipurile suportate - în timp ce calculul real poate fi efectuat într-un tip mai mult sau mai puțin exact [48] .
C++ | |
---|---|
Particularități | |
Unele biblioteci | |
Compilatoare | |
influențat | |
|