C++11

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 15. september 2020; checks kræver 24 redigeringer .

C++11 [1] [2] eller ISO/IEC 14882:2011 [3] (i processen med at arbejde på standarden havde den kodenavnet C++0x [4] [5] ) — en ny version af C++ sprogstandarden i stedet for den tidligere gyldige ISO /IEC 14882:2003. Den nye standard indeholder tilføjelser til kernen af ​​sproget og en udvidelse til standardbiblioteket, inklusive det meste af TR1  - måske undtagen biblioteket med specielle matematiske funktioner. Nye versioner af standarderne, sammen med nogle andre C++-standardiseringsdokumenter, er offentliggjort på ISO C++-udvalgets websted [6] . C++ programmeringseksempler

Programmeringssprog gennemgår en gradvis udvikling af deres muligheder (i øjeblikket, efter C++11, er følgende standardudvidelser blevet offentliggjort: C++14, C++17, C++20). Denne proces forårsager uundgåeligt kompatibilitetsproblemer med eksisterende kode. Appendiks C.2 [diff.cpp03] i Final Draft International Standard N3290 beskriver nogle af  inkompatibiliteterne mellem C++11 og C++03.

Foreslåede ændringer af standarden

Som allerede nævnt vil ændringerne påvirke både C++-kernen og dets standardbibliotek.

Ved udviklingen af ​​hvert afsnit af den fremtidige standard brugte udvalget en række regler:

Der lægges vægt på begyndere, som altid vil udgøre størstedelen af ​​programmører. Mange begyndere søger ikke at uddybe deres viden om C++, idet de begrænser sig til at bruge det, når de arbejder med snævre specifikke opgaver [7] . I betragtning af C++'s alsidighed og bredden af ​​dets brug (inklusive både de mange forskellige applikationer og programmeringsstile), kan selv professionelle finde sig selv nye til nye programmeringsparadigmer .

Udvidelse af kernesproget

Udvalgets primære opgave er at udvikle kernen i C++ sproget. Kernen er blevet væsentligt forbedret, multithreading -understøttelse er blevet tilføjet, understøttelse af generisk programmering er blevet forbedret , initialisering er blevet forenet, og der er blevet arbejdet på at forbedre dens ydeevne.

For nemheds skyld er kernefunktionerne og ændringerne opdelt i tre hoveddele: ydeevneforbedringer, bekvemmelighedsforbedringer og ny funktionalitet. Individuelle elementer kan tilhøre flere grupper, men vil kun blive beskrevet i én - den mest passende.

Ydeevneforbedring

Disse sprogkomponenter introduceres for at reducere hukommelsesomkostninger eller forbedre ydeevnen.

Midlertidige objektreferencer og flyttesemantik

Ifølge C++-standarden kan et midlertidigt objekt , der er et resultat af evalueringen af ​​et udtryk, overføres til funktioner, men kun ved en konstant reference ( const & ). Funktionen er ikke i stand til at bestemme, om det beståede objekt kan betragtes som midlertidigt og modificerbart (et const-objekt, der også kan videregives af en sådan reference, kan ikke ændres (lovligt)). Dette er ikke et problem for simple strukturer som f.eks complex. , men for komplekse typer, der kræver hukommelsesallokering-deallokering, kan ødelæggelse af et midlertidigt objekt og oprettelse af et permanent være tidskrævende, mens man blot kan sende pointere direkte.

C++11 introducerer en ny type reference , rvalue referencen .  Dens erklæring er: type && . Nye regler for overbelastningsopløsning giver dig mulighed for at bruge forskellige overbelastede funktioner til ikke-konstative midlertidige objekter, angivet med rvalues, og for alle andre objekter. Denne innovation gør det muligt at implementere den såkaldte move-semantics .

For eksempel std::vector er en simpel indpakning omkring et C-array og en variabel, der gemmer dens størrelse. Kopikonstruktøren std::vector::vector(const vector &x)vil oprette et nyt array og kopiere informationen; overførselskonstruktøren std::vector::vector(vector &&x)kan simpelthen udveksle pointere og variabler, der indeholder længden.

Annonce eksempel.

skabelon < klasse T > klassevektor _ { vektor ( konst vektor & ); // Kopier konstruktør (langsom) vektor ( vektor && ); // Overfør konstruktør fra et midlertidigt objekt (hurtig) vektor & operator = ( const vektor & ); // Regelmæssig tildeling (langsom) vektor & operator = ( vektor && ); // Flyt midlertidigt objekt (hurtigt) void foo () & ; // Funktion, der kun virker på et navngivet objekt (langsomt) void foo () && ; // Funktion, der kun virker for et midlertidigt objekt (hurtigt) };

Der er flere mønstre forbundet med midlertidige links, hvoraf de to vigtigste er og . Den første gør et regulært navngivet objekt til en midlertidig reference: moveforward

// std::move skabelon eksempel void bar ( std :: string && x ) { statisk std :: stringsomeString ; _ someString = std :: move ( x ); // inde i x=string&-funktionen, deraf det andet træk for at kalde flytteopgaven } std :: snorlige ; _ bar ( std :: flyt ( y )); // første træk forvandler streng& til streng&& til opkaldsbjælken

Skabelonen bruges kun i metaprogrammering, kræver en eksplicit skabelonparameter (den har to overbelastninger, der ikke kan skelnes), og er forbundet med to nye C++-mekanismer. Den første er linklimning: , derefter . For det andet kræver bar()-funktionen ovenfor et midlertidigt objekt på ydersiden, men på indersiden er x-parameteren en almindelig navngivet (lvalue) for fallback, hvilket gør det umuligt automatisk at skelne string&-parameteren fra string&&-parameteren. I en almindelig ikke-skabelonfunktion kan programmøren sætte move(), men hvad med skabelonen? forwardusing One=int&&; using Two=One&;Two=int&

// eksempel på brug af skabelonen std::forward class Obj { std :: stringfield ; _ skabelon < classT > _ Obj ( T && x ) : felt ( std :: fremad < T > ( x )) {} };

Denne konstruktør dækker de almindelige (T=streng&), kopi- (T=const string&) og flytte- (T=streng) overbelastninger med referencelimning. Og frem gør intet eller udvider til std::move afhængigt af typen af ​​T, og konstruktøren vil kopiere, hvis det er en kopi, og flytte, hvis det er en flytning.

Generiske konstantudtryk

C++ har altid haft begrebet konstante udtryk. Således gav udtryk som 3+4 altid de samme resultater uden at forårsage bivirkninger. I sig selv giver konstante udtryk en bekvem måde for C++-kompilere til at optimere resultatet af kompilering. Kompilere evaluerer kun resultaterne af sådanne udtryk på kompileringstidspunktet og gemmer de allerede beregnede resultater i programmet. Sådanne udtryk evalueres således kun én gang. Der er også enkelte tilfælde, hvor sprogstandarden kræver brug af konstante udtryk. Sådanne tilfælde kan for eksempel være definitioner af eksterne arrays eller enum-værdier.


int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // opret en matrix med 12 heltal; forbudt i C++

Ovenstående kode er ulovlig i C++, fordi GiveFive() + 7 ikke teknisk set er et konstant udtryk kendt på kompileringstidspunktet. Compileren ved bare ikke på det tidspunkt, at funktionen faktisk returnerer en konstant ved kørselstid. Årsagen til denne compiler-ræsonnement er, at denne funktion kan påvirke tilstanden af ​​en global variabel, kalde en anden ikke-konst runtime-funktion og så videre.

C++11 introducerer nøgleordet constexpr , som giver brugeren mulighed for at sikre, at enten en funktion eller en objektkonstruktør returnerer en kompileringstidskonstant. Ovenstående kode kan omskrives sådan:

constexpr int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // opret en matrix med 12 heltal; tilladt i C++11

Dette nøgleord giver compileren mulighed for at forstå og verificere, at GiveFive returnerer en konstant.

Brugen af ​​constexpr pålægger meget strenge begrænsninger for funktionens handlinger:

  1. en sådan funktion skal returnere en værdi;
  2. funktionens krop skal have formen returudtryk ;
  3. udtrykket skal bestå af konstanter og/eller kald til andre constexpr- funktioner;
  4. en funktion, der er angivet med constexpr , kan ikke bruges, før den er defineret i den aktuelle kompileringsenhed.

I den tidligere version af standarden kunne kun heltals- eller enum-typevariabler bruges i konstante udtryk. I C++11 ophæves denne begrænsning for variabler, hvis definition er indledt af constexpr-nøgleordet:

constexpr dobbelt accelerationOfGravity = 9,8 ; constexpr dobbeltmåneGravity = accelerationOfGravity / 6 ; _

Sådanne variable anses allerede implicit for at være angivet med nøgleordet const . De kan kun indeholde resultaterne af konstante udtryk eller konstruktørerne af sådanne udtryk.

Hvis det er nødvendigt at konstruere konstante værdier fra brugerdefinerede typer, kan konstruktører af sådanne typer også erklæres ved hjælp af constexpr . En konstant udtrykskonstruktør skal ligesom konstantfunktioner også defineres før dens første brug i den aktuelle kompileringsenhed. En sådan konstruktør skal have en tom krop, og en sådan konstruktør skal initialisere medlemmerne af sin type med kun konstanter.

Ændringer i definitionen af ​​simple data

I standard C++ kan kun strukturer, der opfylder et bestemt sæt regler, betragtes som en almindelig gammel datatype ( POD). Der er gode grunde til at forvente, at disse regler udvides, så flere typer betragtes som POD'er. Typer, der opfylder disse regler, kan bruges i en C-kompatibel objektlagsimplementering, men C++03's liste over disse regler er alt for restriktiv.

C++11 vil lempe adskillige regler vedrørende definitionen af ​​simple datatyper.

En klasse anses for at være en simpel datatype, hvis den er triviel , har et standardlayout ( standardlayout ) , og hvis typerne af alle dens ikke-statiske datamedlemmer også er simple datatyper.

En triviel klasse er en klasse, der:

  1. indeholder en triviel standardkonstruktør,
  2. indeholder ikke ikke-trivielle kopikonstruktører,
  3. indeholder ikke ikke-trivielle bevægelseskonstruktorer,
  4. indeholder ikke ikke-trivielle kopiopgaveoperatører,
  5. indeholder ikke ikke-trivielle bevægelsestildelingsoperatører,
  6. indeholder en triviel destruktor.

En klasse med standardplacering er en klasse, der:

  1. ikke indeholder ikke-statiske datamedlemmer af en specialplaceret klassetype (eller en række elementer af den type) eller en referencetype,
  2. indeholder ikke virtuelle funktioner,
  3. indeholder ikke virtuelle basisklasser,
  4. har den samme tilgængelighedstype ( public, private, protected) for alle ikke-statiske datamedlemmer,
  5. har ikke basisklasser med ikke-standardplacering,
  6. er ikke en klasse, der samtidigt indeholder nedarvede og ikke-nedarvede ikke-statiske datamedlemmer, eller indeholder ikke-statiske datamedlemmer, der er nedarvet fra flere basisklasser på én gang,
  7. har ikke basisklasser af samme type som det første ikke-statiske datamedlem (hvis nogen).

Fremskynd kompileringen

Eksterne skabeloner

I standard C++ skal compileren instansiere en skabelon, når den støder på sin fulde specialisering i en oversættelsesenhed. Dette kan øge kompileringstiden markant, især når skabelonen instansieres med de samme parametre i et stort antal oversættelsesenheder. Der er i øjeblikket ingen måde at fortælle C++, at der ikke skal være nogen instansiering.

C++11 introducerede ideen om eksterne skabeloner. C++ har allerede en syntaks til at fortælle compileren, at en skabelon skal instansieres på et bestemt tidspunkt:

skabelon klasse std :: vektor < MyClass > ;

C++ mangler evnen til at forhindre compileren i at instansiere en skabelon i en oversættelsesenhed. C++11 udvider simpelthen denne syntaks:

ekstern skabelon klasse std :: vektor < MyClass > ;

Dette udtryk fortæller compileren ikke at instansiere skabelonen i denne oversættelsesenhed.

Forbedret brugervenlighed

Disse funktioner er beregnet til at gøre sproget lettere at bruge. De giver dig mulighed for at styrke typesikkerheden, minimere kodeduplikering, gøre det sværere for kode at blive misbrugt, og så videre.

Initialiseringslister

Begrebet initialiseringslister kom til C++ fra C. Ideen er, at en struktur eller et array kan oprettes ved at sende en liste med argumenter i samme rækkefølge, som medlemmerne af strukturen er defineret. Initialiseringslister er rekursive, hvilket gør det muligt at bruge dem til arrays af strukturer og strukturer, der indeholder indlejrede strukturer.

struct objekt { flyde først ; int anden ; }; Objekt skalar = { 0.43f , 10 }; // ét objekt, med first=0.43f og second=10 Object anArray [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // række af tre objekter

Initialiseringslister er meget nyttige til statiske lister, og når du vil initialisere en struktur til en bestemt værdi. C++ indeholder også konstruktører, som kan indeholde det generelle arbejde med at initialisere objekter. C++-standarden tillader brug af initialiseringslister for strukturer og klasser, forudsat at de er i overensstemmelse med POD-definitionen (Plain Old Data). Ikke-POD-klasser kan ikke bruge initialiseringslister til initialisering, inklusive standard C++-beholdere såsom vektorer.

C++11 har forbundet konceptet med initialiseringslister og en skabelonklasse kaldet std::initializer_list . Dette gjorde det muligt for konstruktører og andre funktioner at modtage initialiseringslister som parametre. For eksempel:

klasse SequenceClass { offentligt : SequenceClass ( std :: initializer_list < int > list ); };

Denne beskrivelse giver dig mulighed for at oprette en SequenceClass ud fra en sekvens af heltal som følger:

SequenceClass someVar = { 1 , 4 , 5 , 6 };

Dette demonstrerer, hvordan en speciel slags konstruktør fungerer for en initialiseringsliste. Klasser, der indeholder sådanne konstruktører, behandles på en særlig måde under initialisering (se nedenfor ).

Klassen std::initializer_list<> er defineret i C++11 Standard Library. Objekter af denne klasse kan dog kun oprettes statisk af C++11-kompileren ved hjælp af {}-parentessyntaksen. Listen kan kopieres efter oprettelse, dog vil dette være kopi-for-henvisning. Initialiseringslisten er const: hverken dens medlemmer eller deres data kan ændres efter oprettelsen.

Fordi std::initializer_list<> er en fuldgyldig type, kan den bruges i mere end blot konstruktører. Almindelige funktioner kan tage indtastede initialiseringslister som et argument, for eksempel:

void Funktionsnavn ( std :: initializer_list < float > list ); Funktionsnavn ({ 1.0f , -3.45f , -0.4f });

Standardbeholdere kan initialiseres på denne måde:

std :: vektor < std :: streng > v = { "xyzzy" , "plugh" , "abracadabra" }; std :: vektor < std :: streng > v { "xyzzy" , "plugh" , "abracadabra" }; Generisk initialisering

C++-standarden indeholder en række problemer relateret til typeinitialisering. Der er flere måder at initialisere typer på, og ikke alle fører til de samme resultater. For eksempel kan den traditionelle syntaks for en initialiserende konstruktør ligne en funktionsdeklaration, og der skal udvises ekstra forsigtighed for at forhindre compileren i at analysere den forkert. Kun aggregattyper og POD-typer kan initialiseres med aggregatinitialiserere (af typen SomeType var = {/*stuff*/};).

C++11 giver en syntaks, der gør det muligt at bruge en enkelt form for initialisering til alle slags objekter ved at udvide initialiseringslistens syntaks:

struct BasicStruct { int x ; dobbelt y ; }; struct AltStruct { AltStruct ( int x , double y ) : x_ ( x ), y_ ( y ) {} privat : int x_ ; dobbelt y_ ; }; BasicStruct var1 { 5 , 3.2 }; AltStruct var2 { 2 , 4.3 };

Initialisering af var1 fungerer nøjagtigt på samme måde som initialisering af aggregater, det vil sige, at hvert objekt initialiseres ved at kopiere den tilsvarende værdi fra initialiseringslisten. Om nødvendigt vil implicit typekonvertering blive anvendt. Hvis den ønskede transformation ikke eksisterer, vil kildekoden blive betragtet som ugyldig. Under initialiseringen af ​​var2 vil konstruktøren blive kaldt.

Det er muligt at skrive kode som denne:

struktur IdString { std :: strengnavn ; _ int identifikator ; }; IdString GetString () { returner { "SomeName" , 4 }; // Bemærk manglen på eksplicitte typer }

Generisk initialisering erstatter ikke fuldstændigt konstruktørinitialiseringssyntaks. Hvis en klasse har en konstruktør, der tager en initialiseringsliste ( TypeName(initializer_list<SomeType>); ) som et argument, vil den have forrang over andre muligheder for oprettelse af objekter. For eksempel, i C++11 indeholder std::vector en konstruktør, der tager en initialiseringsliste som et argument:

std :: vektor < int > theVec { 4 };

Denne kode vil resultere i et konstruktørkald, der tager en initialiseringsliste som et argument, snarere end en én-parameter konstruktør, der opretter en container af den givne størrelse. For at kalde denne konstruktør skal brugeren bruge standard konstruktørinvokationssyntaksen.

Indtast inferens

I standard C++ (og C) skal typen af ​​en variabel angives eksplicit. Men med fremkomsten af ​​skabelontyper og skabelonmetaprogrammeringsteknikker kan typen af ​​nogle værdier, især funktionsreturværdier, ikke let specificeres. Dette fører til vanskeligheder med at lagre mellemliggende data i variabler, nogle gange kan det være nødvendigt at kende den interne struktur af et bestemt metaprogrammeringsbibliotek.

C++11 tilbyder to måder at afhjælpe disse problemer på. For det første kan definitionen af ​​en eksplicit initialiserbar variabel indeholde nøgleordet auto . Dette vil resultere i oprettelsen af ​​en variabel af typen initialiseringsværdi:

auto someStrangeCallableType = std :: bind ( & SomeFunction , _2 , _1 , someObject ); auto andenVariabel = 5 ;

Typen someStrangeCallableType bliver den type, som den konkrete implementering af skabelonfunktionen returnerer std::bindfor de givne argumenter. Denne type vil let blive bestemt af compileren under semantisk analyse, men programmøren ville være nødt til at lave noget research for at bestemme typen.

Den andenVariable -type er også veldefineret, men kan lige så nemt defineres af programmøren. Denne type er int , det samme som en heltalskonstant.

Derudover kan nøgleordet decltype bruges til at bestemme typen af ​​et udtryk på kompileringstidspunktet . For eksempel:

int nogleInt ; decltype ( someInt ) otherIntegerVariable = 5 ;

Brug af decltype er mest nyttigt i forbindelse med auto , da typen af ​​en variabel, der er erklæret som auto , kun er kendt af compileren. Brug af decltype kan også være ret nyttigt i udtryk, der bruger operatøroverbelastning og skabelonspecialisering.

autokan også bruges til at reducere koderedundans. For eksempel i stedet for:

for ( vektor < int >:: const_iterator itr = myvec . cbegin (); itr != myvec . cend (); ++ itr )

programmøren kan skrive:

for ( auto itr = myvec . cbegin (); itr != myvec . cend (); ++ itr )

Forskellen bliver især mærkbar, når en programmør bruger et stort antal forskellige containere, selvom der stadig er en god måde at reducere overflødig kode ved at bruge typedef.

En type, der er markeret med decltype , kan være forskellig fra den type, der udledes med auto .

#inkluder <vektor> int main () { const std :: vektor < int > v ( 1 ); auto a = v [ 0 ]; // type a - int decltype ( v [ 0 ]) b = 1 ; // type b - const int& (returværdi // std::vector<int>::operator[](size_type) const) auto c = 0 ; // skriv c - int auto d = c ; // type d - int decltype ( c ) e ; // type e - int, type enhed med navnet c decltype (( c )) f = c ; // type f er int& fordi (c) er en lværdi decltype ( 0 ) g ; // type g er int, da 0 er en rværdi } For-loop gennem en samling

I standard C++ kræver det en masse kode at iterere over elementerne i en samling . Nogle sprog, såsom C# , har faciliteter, der giver en " foreach " -sætning , der automatisk går gennem elementerne i en samling fra start til slut. C++11 introducerer en lignende facilitet. For - sætningen gør det lettere at iterere over en samling af elementer:

int my_array [ 5 ] = { 1 , 2 , 3 , 4 , 5 }; for ( int & x : my_array ) { x *= 2 ; }

Denne form for for, kaldet "range-based for" på engelsk, vil besøge hvert element i samlingen. Dette gælder for C -arrays , initialiseringslister og alle andre typer , der har funktioner, begin()og end()som returnerer iteratorer . Alle containere i standardbiblioteket , der har et start/slut-par, vil arbejde med en for-erklæring på samlingen.

En sådan cyklus vil også fungere, for eksempel med C-lignende arrays, fordi C++11 introducerer kunstigt de nødvendige pseudo-metoder for dem (begyndelse, slutning og nogle andre).

// interval-baseret gennemgang af den klassiske matrix int arr1 [] = { 1 , 2 , 3 }; for ( auto el : arr1 ); Lambda funktioner og udtryk

I standard C++, for eksempel, når man bruger standard C++ biblioteksalgoritmerne sorter og find , er der ofte behov for at definere prædikatfunktioner i nærheden af, hvor algoritmen kaldes. Der er kun én mekanisme i sproget til dette: evnen til at definere en funktionsklasse (det er forbudt at overføre en forekomst af en klasse defineret inde i en funktion til algoritmer (Meyers, Effektiv STL)). Ofte er denne metode for overflødig og omfattende og gør det kun svært at læse koden. Derudover tillader standard C++-reglerne for klasser defineret i funktioner ikke, at de kan bruges i skabeloner og gør dem dermed umulige at bruge.

Den åbenlyse løsning på problemet var at tillade definitionen af ​​lambda-udtryk og lambda-funktioner i C++11. Lambdafunktionen er defineret sådan:

[]( int x , int y ) { return x + y ; }

Returtypen for denne unavngivne funktion beregnes som decltype(x+y) . Returtypen kan kun udelades, hvis lambda-funktionen er af formen . Dette begrænser størrelsen af ​​lambda-funktionen til et enkelt udtryk. return expression

Returtypen kan angives eksplicit, for eksempel:

[]( int x , int y ) -> int { int z = x + y ; returnere z ; }

Dette eksempel opretter en midlertidig variabel z for at gemme en mellemværdi. Som med normale funktioner bevares denne mellemværdi ikke mellem opkald.

Returtypen kan helt udelades, hvis funktionen ikke returnerer en værdi (det vil sige, at returtypen er void )

Det er også muligt at bruge referencer til variable defineret i samme omfang som lambda-funktionen. Et sæt af sådanne variabler kaldes normalt en lukning . Lukninger defineres og bruges som følger:

std :: vektor < int > someList ; int total = 0 ; std :: for_each ( someList . begin (), someList . end (), [ & total ]( int x ) { total += x ; }); std :: cout << total ;

Dette vil vise summen af ​​alle elementer på listen. Den samlede variabel gemmes som en del af lambdafunktionens lukning. Fordi den refererer til stackvariablen total , kan den ændre dens værdi.

Lukningsvariabler for lokale variabler kan også defineres uden brug af referencesymbolet & , hvilket betyder at funktionen kopierer værdien. Dette tvinger brugeren til at erklære en hensigt om at henvise til eller kopiere en lokal variabel.

For lambda-funktioner, der med garanti udføres i deres omfang, er det muligt at bruge alle stakvariabler uden behov for eksplicitte referencer til dem:

std :: vektor < int > someList ; int total = 0 ; std :: for_each ( someList . begin (), someList . end (), [ & ]( int x ) { total += x ; });

Implementeringsmetoder kan variere internt, men lambda-funktionen forventes at gemme en pointer til stakken af ​​den funktion, den blev oprettet i, i stedet for at operere på individuelle stackvariablereferencer.

[&]Hvis det bruges i stedet [=], vil alle anvendte variable blive kopieret, hvilket gør det muligt at bruge lambda-funktionen uden for de oprindelige variables omfang.

Standardoverførselsmetoden kan også suppleres med en liste over individuelle variabler. For eksempel, hvis du skal videregive de fleste variabler ved reference, og en efter værdi, kan du bruge følgende konstruktion:

int total = 0 ; int værdi = 5 ; [ & , værdi ]( int x ) { total += ( x * værdi ); } ( 1 ); //(1) Kald lambda-funktion med værdi 1

Dette vil medføre, at total overføres ved reference og værdi efter værdi.

Hvis en lambda-funktion er defineret i en klassemetode, betragtes den som en ven af ​​den klasse. Sådanne lambda-funktioner kan bruge en reference til et objekt af klassetypen og få adgang til dets interne felter:

[]( SomeType * typePtr ) { typePtr -> SomePrivateMemberFunction (); }

Dette vil kun fungere, hvis omfanget af lambda-funktionen er en klassemetode SomeType .

Arbejdet med denne pointer til det objekt, som den aktuelle metode interagerer med, implementeres på en særlig måde. Det skal udtrykkeligt markeres i lambda-funktionen:

[ this ]() { this -> SomePrivateMemberFunction (); }

Brug af en formular [&]eller [=]en lambda-funktion gør denne tilgængelig automatisk.

Typen af ​​lambda-funktioner er implementeringsafhængig; navnet på denne type er kun tilgængeligt for compileren. Hvis du skal sende en lambda-funktion som en parameter, skal den være en skabelontype eller lagres ved hjælp af std::function . Autonøgleordet giver dig mulighed for at gemme en lambda-funktion lokalt :

auto myLambdaFunc = [ this ]() { this -> SomePrivateMemberFunction (); };

Derudover, hvis funktionen ikke tager nogen argumenter, kan ()du udelade:

auto myLambdaFunc = []{ std :: cout << "hej" << std :: endl ; }; Alternativ funktionssyntaks

Nogle gange er der behov for at implementere en funktionsskabelon, der ville resultere i et udtryk, der har samme type og samme værdikategori som et andet udtryk.

skabelon < typenavn LHS , typenavn RHS > RETURN_TYPE AddingFunc ( const LHS & lhs , const RHS & rhs ) // hvad skal RETURN_TYPE være? { retur lhs + rhs ; }

For at udtrykket AddingFunc(x, y) skal have samme type og samme værdikategori som udtrykket lhs + rhs , når de gives argumenterne x og y , kan følgende definition bruges i C++11:

skabelon < typenavn LHS , typenavn RHS > decltype ( std :: declval < const LHS &> () + std :: declval < const RHS &> ()) AddingFunc ( const LHS & lhs , const RHS & rhs ) { retur lhs + rhs ; }

Denne notation er noget besværlig, og det ville være rart at kunne bruge lhs og rhs i stedet for henholdsvis std::declval<const LHS &>() og std::declval<const RHS &>(). Dog i næste version

skabelon < typenavn LHS , typenavn RHS > decltype ( lhs + rhs ) AddingFunc ( const LHS & lhs , const RHS & rhs ) // Ikke gyldig i C++11 { retur lhs + rhs ; }

mere læsbare for mennesker kan lhs- og rhs-identifikatorerne, der bruges i decltype -operanden, ikke angive muligheder, der er erklæret senere. For at løse dette problem introducerer C++11 en ny syntaks til at erklære funktioner med en returtype i slutningen:

skabelon < typenavn LHS , typenavn RHS > auto AddingFunc ( const LHS & lhs , const RHS & rhs ) -> decltype ( lhs + rhs ) { retur lhs + rhs ; }

Det skal dog bemærkes, at i den mere generiske AddingFunc-implementering nedenfor, har den nye syntaks ikke fordel af korthed:

skabelon < typenavn LHS , typenavn RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: fremad < LHS > ( lhs ) + std :: fremad < RHS > ( rhs )) { return std :: fremad < LHS > ( lhs ) + std :: fremad < RHS > ( rhs ); } skabelon < typenavn LHS , typenavn RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // samme effekt som med std::forward ovenfor { return std :: fremad < LHS > ( lhs ) + std :: fremad < RHS > ( rhs ); } skabelon < typenavn LHS , typenavn RHS > decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // samme effekt som at sætte type i slutningen AddingFunc ( LHS && lhs , RHS && rhs ) { return std :: fremad < LHS > ( lhs ) + std :: fremad < RHS > ( rhs ); }

Den nye syntaks kan bruges i enklere erklæringer og erklæringer:

struct SomeStruct { auto FuncName ( int x , int y ) -> int ; }; auto SomeStruct :: FuncName ( int x , int y ) -> int { returner x + y _ }

Brugen af ​​nøgleordet " " autobetyder i dette tilfælde kun en sen indikation af returtypen og er ikke relateret til dens automatiske slutning.

Forbedring af objektkonstruktører

Standard C++ tillader ikke, at en klassekonstruktør kaldes fra en anden konstruktør af samme klasse; hver konstruktør skal fuldt initialisere alle medlemmer af klassen eller kalde klassens metoder for at gøre det. Ikke-konst-medlemmer af en klasse kan ikke initialiseres på det sted, hvor disse medlemmer er erklæret.

C++11 slipper af med disse problemer.

Den nye standard gør det muligt at kalde en klassekonstruktør fra en anden (den såkaldte delegation). Dette giver dig mulighed for at skrive konstruktører, der bruger adfærd fra andre konstruktører uden at indføre duplikatkode.

Eksempel:

klasse SomeType { int nummer ; offentligt : SomeType ( int new_number ) : number ( new_number ) {} SomeType () : SomeType ( 42 ) {} };

Fra eksemplet kan du se, at konstruktøren SomeTypeuden argumenter kalder konstruktøren af ​​den samme klasse med et heltalsargument for at initialisere variablen number. En lignende effekt kunne opnås ved at angive en startværdi på 42 for denne variable ret ved dens erklæring.

klasse SomeType { int tal = 42 ; offentligt : SomeType () {} eksplicit SomeType ( int new_number ) : number ( new_number ) {} };

Enhver klassekonstruktør vil initialisere numbertil 42, hvis den ikke selv tildeler en anden værdi til den.

Java , C# og D er eksempler på sprog, der også løser disse problemer .

Det skal bemærkes, at hvis et objekt i C++03 anses for at være fuldt oprettet, når dets konstruktør afslutter eksekveringen, så vil resten af ​​konstruktørerne i C++11, efter at mindst én delegerende konstruktør er blevet udført, arbejde på et fuldt konstrueret objekt. På trods af dette vil objekterne i den afledte klasse kun blive konstrueret, efter at alle konstruktørerne af basisklasserne er blevet udført.

Eksplicit substitution af virtuelle funktioner og endelighed

Det er muligt, at signaturen for en virtuel metode er blevet ændret i basisklassen eller forkert indstillet i den afledte klasse oprindeligt. I sådanne tilfælde vil den givne metode i den afledte klasse ikke tilsidesætte den tilsvarende metode i basisklassen. Så hvis programmøren ikke ændrer metodesignaturen korrekt i alle afledte klasser, kaldes metoden muligvis ikke korrekt under programafvikling. For eksempel:

struct Base { virtual void some_func (); }; struct Afledt : Base { void sone_func (); };

Her er navnet på en virtuel funktion erklæret i en afledt klasse stavet forkert, så en sådan funktion vil ikke tilsidesætte Base::some_func, og vil derfor ikke blive kaldt polymorf gennem en pointer eller reference til grundsubobjektet.

C++11 vil tilføje muligheden for at spore disse problemer på kompileringstidspunktet (i stedet for køretid). For bagudkompatibilitet er denne funktion valgfri. Den nye syntaks er vist nedenfor:

struktur B { virtual void some_func (); virtuelt tomrum f ( int ); virtuel void g () const ; }; struktur D1 : offentlig B { void sone_func () tilsidesætte ; // fejl: ugyldigt funktionsnavn void f ( int ) tilsidesættelse ; // OK: tilsidesætter den samme funktion i basisklassen virtual void f ( long ) override ; // fejl: parameter type mismatch virtual void f ( int ) const override ; // fejl: funktion cv-kvalifikation mismatch virtuel int f ( int ) tilsidesættelse ; // fejl: returtype mismatch virtual void g () const final ; // OK: tilsidesætter den samme funktion i basisklassen virtual void g ( long ); // OK: ny virtuel funktion }; struktur D2 : D1 { virtuel void g () const ; // fejl: forsøg på at erstatte den endelige funktion };

Tilstedeværelsen af ​​en specifikation for en virtuel funktion finalbetyder, at dens yderligere udskiftning er umulig. En klasse defineret med den endelige specificator kan heller ikke bruges som en basisklasse:

struct F final { int x , y ; }; struct D : F // fejl: arv fra afsluttende klasser er ikke tilladt { int z ; };

Identifikatorerne og har kun en overridesærlig finalbetydning, når de bruges i visse situationer. I andre tilfælde kan de bruges som normale identifikatorer (for eksempel som navnet på en variabel eller funktion).

Nul pointer konstant

Siden fremkomsten af ​​C i 1972 har konstanten 0 spillet den dobbelte rolle som et heltal og en nul-pointer. En måde at håndtere denne tvetydighed, der er iboende i C-sproget, er makroen NULL, som typisk udfører ((void*)0)eller -substitutionen 0. C++ adskiller sig fra C i denne henseende og tillader kun brugen 0af ​​en nul-pointer som en konstant. Dette fører til dårlig interaktion med funktionsoverbelastning:

void foo ( char * ); void foo ( int );

Hvis makroen NULLer defineret som 0(hvilket er almindeligt i C++), vil linjen foo(NULL);resultere i et opkald foo(int), ikke foo(char *)som et hurtigt kig på koden kunne antyde, hvilket næsten helt sikkert ikke er, hvad programmøren havde til hensigt.

En af nyhederne ved C++11 er et nyt nøgleord til at beskrive en nul-pointer-konstant - nullptr. Denne konstant er af typen std::nullptr_t, som implicit kan konverteres til typen af ​​enhver pointer og sammenlignes med enhver pointer. Implicit konvertering til en integraltype er ikke tilladt, undtagen for bool. Det oprindelige forslag til standarden tillod ikke implicit konvertering til boolesk, men standardudkastgruppen tillod sådanne konverteringer af hensyn til kompatibilitet med konventionelle pointertyper. Den foreslåede formulering blev ændret efter en enstemmig afstemning i juni 2008 [1] .

For bagudkompatibilitet kan en konstant 0også bruges som en nul-pointer.

char * pc = nullptr ; // true int * pi = nullptr ; // true bool b = nullptr ; // ret. b=falsk. int i = nullptr ; // fejl foo ( nullptr ); // kalder foo(char *), ikke foo(int);

Ofte er konstruktioner, hvor viseren med garanti er tom, enklere og mere sikre end resten - så du kan overbelaste med . nullptr_t

klasse Nyttelast ; klasse SmartPtr { SmartPtr () = default ; SmartPtr ( nullptr_t ) {} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< eksplicit SmartPtr ( Nyttelast * aData ) : fData ( aData ) {} // kopier konstruktører og op= udelad ~ SmartPtr () { delete fData ; } privat : Nyttelast * fData = nullptr ; } SmartPtr getPayload1 () { return nullptr ; } // SmartPtr(nullptr_t) overbelastning vil blive kaldt. Stærkt indtastede enums

I standard C++ er enums ikke typesikre. Faktisk er de repræsenteret af heltal, på trods af at selve typerne af opregninger er forskellige fra hinanden. Dette gør det muligt at foretage sammenligninger mellem to værdier fra forskellige enums. Den eneste mulighed, som C++03 tilbyder for at beskytte enums, er ikke implicit at konvertere heltal eller elementer af en enum til elementer af en anden enum. Den måde, den er repræsenteret i hukommelsen (heltalstype), er også implementeringsafhængig og derfor ikke bærbar. Endelig har opregningselementer et fælles omfang, hvilket gør det umuligt at oprette elementer med samme navn i forskellige opregninger.

C++11 tilbyder en særlig klassificering af disse enums, fri for ovennævnte ulemper. For at beskrive sådanne opregninger bruges en erklæring enum class(den kan også bruges enum structsom et synonym):

enum class enumeration { Val1 , Val2 , Val3 = 100 , Val4 , /* = 101 */ };

En sådan opregning er typesikker. Elementer i en klasseenum kan ikke implicit konverteres til heltal. Som en konsekvens er sammenligning med heltal også umulig (udtrykket Enumeration::Val4 == 101resulterer i en kompileringsfejl).

Klasseopregningstypen er nu implementeringsuafhængig. Som standard, som i tilfældet ovenfor, er denne type int, men i andre tilfælde kan typen indstilles manuelt som følger:

enum klasse Enum2 : unsigned int { Val1 , Val2 };

Omfanget af enum-medlemmer bestemmes af omfanget af enum-navnet. Brug af elementnavne kræver specificering af navnet på klassens enum. Så for eksempel er værdien Enum2::Val1defineret, men værdien Val1 er ikke defineret.

Derudover giver C++11 muligheden for eksplicit at scoping og underliggende typer for almindelige enums:

enum Enum3 : unsigned long { Val1 = 1 , Val2 };

I dette eksempel er enum-elementnavnene defineret i enum-rummet (Enum3::Val1), men for bagudkompatibilitet er elementnavnene også tilgængelige i det fælles omfang.

Også i C++11 er det muligt at foruderklære enums. I tidligere versioner af C++ var dette ikke muligt, fordi størrelsen af ​​en enum afhang af dens elementer. Sådanne erklæringer kan kun bruges, når størrelsen af ​​opregningen er angivet (eksplicit eller implicit):

enum Enum1 ; // ugyldig for C++ og C++11; underliggende type kan ikke bestemmes enum Enum2 : unsigned int ; // true for C++11, underliggende type eksplicit specificeret enum -klasse Enum3 ; // true for C++11, den underliggende type er int enum klasse Enum4 : unsigned int ; // sand for C++11. enum Enum2 : unsigned short ; // ugyldig for C++11, fordi Enum2 tidligere blev erklæret med en anden underliggende type Vinkelparenteser

Standard C++-parsere definerer altid ">>"-tegnkombinationen som den højre skiftoperator. Fraværet af et mellemrum mellem de afsluttende vinkelparenteser i skabelonparametrene (hvis de er indlejret) behandles som en syntaksfejl.

C++11 forbedrer opførselen af ​​parseren i dette tilfælde, så flere retvinklede parenteser vil blive fortolket som afsluttende skabelonargumentlister.

Den beskrevne adfærd kan rettes til fordel for den gamle tilgang ved hjælp af parenteser.

skabelon < klasse T > klasse Y { /* ... */ }; Y < X < 1 >> x3 ; // Korrekt, samme som "Y<X<1> > x3;". Y < X < 6 >> 1 >> x4 ; // Syntaks fejl. Du skal skrive "Y<X<(6>>1)>> x4;".

Som vist ovenfor er denne ændring ikke helt kompatibel med den tidligere standard.

Eksplicitte konverteringsoperatorer

C++-standarden giver nøgleordet explicitsom en modifikator for én-parameter-konstruktører, så sådanne konstruktører ikke fungerer som implicitte konverterings-konstruktører. Dette påvirker dog ikke de faktiske konverteringsoperatører på nogen måde. For eksempel kan en smart pointer-klasse indeholde operator bool()for at efterligne en normal pointer. En sådan operator kan for eksempel kaldes sådan: if(smart_ptr_variable)(grenen udføres, hvis markøren ikke er nul). Problemet er, at en sådan operatør ikke beskytter mod andre uventede konverteringer. Da typen booler erklæret som en aritmetisk type i C++, er implicit konvertering til enhver heltalstype eller endda til en flydende kommatype mulig, hvilket igen kan føre til uventede matematiske operationer.

I C++11 explicitgælder nøgleordet også for konverteringsoperatører. Ligesom konstruktører beskytter den mod uventede implicitte konverteringer. Situationer, hvor sproget kontekstuelt forventer en boolsk type (for eksempel i betingede udtryk, sløjfer og logiske operatoroperander) betragtes som eksplicitte konverteringer, og den eksplicitte bool-konverteringsoperator påkaldes direkte.

Skabelon typedef

I standard C++ kan et nøgleord typedefkun bruges som synonymdefinition for en anden type, herunder som synonym for en skabelonspecifikation med alle dens parametre specificeret. Men det er ikke muligt at oprette et skabelonsynonym. For eksempel:

skabelon < typenavn Først , typenavn Anden , int tredje > klasse SomeType ; skabelon < typenavn Anden > _ typedef SomeType < OtherType , Second , 5 > TypedefName ; // Ikke muligt i C++

Dette vil ikke kompilere.

C++11 tilføjede denne funktion med følgende syntaks:

skabelon < typenavn Først , typenavn Anden , int tredje > klasse SomeType ; skabelon < typenavn Anden > _ ved at bruge TypedefName = SomeType < OtherType , Second , 5 > ;

I C++11 kan direktivet usingogså bruges til at kalde en datatype.

typedef void ( * OtherType )( double ); // Gammel stil ved hjælp af OtherType = void ( * )( double ); // Ny syntaks Fjernelse af restriktioner fra fagforening

I tidligere C++-standarder er der en række restriktioner for brugen af ​​medlemmer af klassetyper inden for fagforeninger. Især kan fagforeninger ikke indeholde objekter med en ikke-triviel konstruktør. C++11 fjerner nogle af disse begrænsninger. [2]

Her er et simpelt eksempel på en joinforbindelse, der er tilladt i C++11:

//for placering ny #include <ny> structPoint { _ Punkt () {} Punkt ( int x , int y ) : x_ ( x ), y_ ( y ) {} int x_ , y_ ; }; fagforening U { int z ; dobbelt w ; Punkt p ; // Ikke sandt for C++03, fordi Point har en ikke-trivial konstruktør. Koden fungerer dog korrekt i C++11. U () { new ( & p ) Punkt (); } // Ingen ikke-trivielle metoder er defineret for foreningen. // Hvis det er nødvendigt, kan de fjernes for at få den manuelle definition til at fungere };

Ændringerne påvirker ikke eksisterende kode, da de kun løsner eksisterende begrænsninger.

Udvidelse af funktionaliteten

Dette afsnit beskriver nye funktioner, der tidligere ikke var tilgængelige eller krævede særlige ikke-bærbare biblioteker.

Variable argumentskabeloner

Før C++11 kunne skabeloner (af klasser eller funktioner) kun tage et sæt antal argumenter, defineret da skabelonen oprindeligt blev erklæret. C++11 giver dig mulighed for at definere skabeloner med et variabelt antal argumenter af enhver type.

skabelon < typenavn ... Værdier > klasse tuple ;

For eksempel accepterer skabelonklassen tuple ( tuple ) et vilkårligt antal typenavne som skabelonparametre:

klasse tuple < int , std :: vektor < int > , std :: map < std :: streng , std :: vektor < int >>> some_instance_name ;

Argumenter kan mangle, så muligheden class tuple<> some_instance_namevil også virke.

For at forhindre skabeloninstansering uden argumenter kan følgende definition bruges:

skabelon < typenavn Først typenavn ... Rest > klasse tuple ; _

Variable-argument-skabeloner er også anvendelige til funktioner, hvilket gør det muligt at bruge dem i typesikre varianter af variadiske funktioner (såsom printf) og til håndtering af ikke-trivielle objekter.

skabelon < typenavn ... Params > void printf ( const std :: string & str_format , Params ... parametre );

Operatøren ... spiller her to roller. Til venstre for Params annoncerer en operatør behovet for at pakke parametre. Ved at bruge pakkede parametre kan 0 eller flere argumenter knyttes til en skabelon. Pakkede parametre kan bruges til mere end blot at sende typenavne. Operatoren ... til højre udpakker til gengæld parametrene i separate argumenter (se args...funktionsteksten i eksemplet nedenfor).

Det er også muligt rekursivt at bruge skabeloner med et variabelt antal argumenter. Et eksempel ville være den typesikre erstatning for printf :

void printf ( const char * s ) { mens ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) throw std :: runtime_error ( "ugyldig formatstreng: manglende argumenter" ); std :: cout << * s ++ ; } } skabelon < typenavn T , typenavn ... Args > void printf ( const char * s , T - værdi , Args ... args ) { mens ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) { std :: cout << værdi ; ++ s ; printf ( s , args ...); // fortsæt med at behandle argumenter, selvom *s == 0 returnerer ; } std :: cout << * s ++ ; } throw std :: logic_error ( "ekstra argumenter leveret til printf" ); }

Dette mønster er rekursivt. Bemærk, at printf-funktionen kalder resultaterne af instansieringen af ​​sig selv eller basisprintf-funktionen, hvis args... er tom.

Der er ingen nem måde at omgå parametre i en variadisk skabelon. På trods af dette omgår dette problem ved at bruge argumentet udpakningsoperatør.

For eksempel kan en klasse defineres sådan:

skabelon < typenavn ... BaseClasses > klasse Klassenavn : public BaseClasses ... { offentligt : Klassenavn ( BaseClasses && ... base_classes ) : BaseClasses ( base_classes )... {} };

Udpakningsoperatøren vil duplikere alle typer overordnede klasser ClassNamepå en sådan måde, at klassen vil blive nedarvet fra alle typer specificeret i skabelonparametrene. Derudover skal konstruktøren acceptere en reference til alle basisklasser, så hver overordnet basisklasse initialiseres ClassName.

Skabelonparametre kan omdirigeres. Kombineret med rvalue referencer (se ovenfor), kan du omdirigere:

skabelon < typenavn TypeToConstruct > struct SharedPtrAllocator { skabelon < typenavn ... Args > std :: shared_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( ny TypeToConstruct ( std :: forward < Args > ( params )...)); }; };

Denne kode pakker argumentlisten ud i TypeToConstruct-konstruktøren. Syntaksen std::forward<Args>(params)giver dig mulighed for helt gennemsigtigt at omdirigere argumenter til konstruktøren, uanset deres rvalue-karakter. Funktionen pakker automatisk pointere ind std::shared_ptrfor at give beskyttelse mod hukommelseslækager.

Det er også muligt at angive antallet af pakkede argumenter som følger:

skabelon < typenavn ... Args > struct SomeStruct { static const int size = sizeof ...( Args ); };

Her SomeStruct<Type1, Type2>::sizeer det lig med 2 og SomeStruct<>::sizelig med 0.

Nye strenge bogstaver

C++03 tilbød to typer strenge bogstaver. Den første type, en streng med dobbelte citater, er en nultermineret matrix af typen const char. Den anden type, defineret som L"", er en nultermineret række af type const wchar_t, hvor wchar_tder er en bred karakter af ubestemte størrelser og semantik. Ingen af ​​de bogstavelige typer er beregnet til at understøtte UTF-8 , UTF-16 strengliteraler eller nogen anden type Unicode - kodning

Typedefinitionen charer blevet ændret til eksplicit at sige, at den er mindst den størrelse, der er nødvendig for at gemme en otte-bit UTF-8- kodning , og stor nok til at indeholde et hvilket som helst tegn i runtime-tegnsættet. Tidligere i standarden blev denne type defineret som et enkelt tegn, senere, efter C-sprogstandarden, blev den garanteret at optage mindst 8 bit.

Der er tre Unicode-kodninger, der understøttes i C++11-standarden: UTF-8 , UTF-16 og UTF-32 . Ud over ovenstående ændringer af den indbyggede tegntype chartilføjer C++11 to nye tegntyper: char16_tog char32_t. De er designet til at gemme henholdsvis UTF-16- og UTF-32-tegn.

Det følgende viser, hvordan man opretter strengliteraler for hver af disse kodninger:

u8 "Jeg er en UTF-8-streng." u "Dette er en UTF-16-streng." U "Dette er en UTF-32-streng."

Typen af ​​den første række er normal const char[]. Typen af ​​den anden linje er const char16_t[]. Typen af ​​den tredje linje er const char32_t[].

Når man konstruerer strengliteraler i Unicode-standarden, er det ofte nyttigt at indsætte Unicode-koden direkte i strengen. C++11 giver følgende syntaks til dette:

u8 "Dette er et Unicode-tegn: \u2018 ." u "Dette er et større Unicode-tegn: \u2018 ." U "Dette er et Unicode-tegn: \U00002018 ."

Tallet efter \uskal være hexadecimalt; ingen grund til at bruge præfikset 0x. Identifikationen \ubetyder en 16-bit Unicode-kode; for at indtaste en 32-bit kode, \Ubruges også et 32-bit hexadecimalt tal. Kun gyldige Unicode-koder kan indtastes. For eksempel er koder i området U+D800-U+DFFF ikke tilladt, fordi de er reserveret til UTF-16 surrogatpar.

Det er også nogle gange nyttigt at undgå manuelt at undslippe strenge, især når du bruger XML -filbogstaver, scriptsprog eller regulære udtryk. Til disse formål understøtter C++11 "rå" strenge bogstaver:

R"(The String Data \ Stuff " )" R"delimiter(The String Data \ Stuff " )delimiter"

I det første tilfælde er alt mellem "(og )"en del af strengen. Karaktererne "og \behøver ikke at blive undslippet. I det andet tilfælde "delimiter(starter den en streng, og den slutter først, når den når )delimiter". Strengen delimiterkan være en hvilken som helst streng på op til 16 tegn, inklusive den tomme streng. Denne streng må ikke indeholde mellemrum, kontroltegn, ' (', ' )' eller tegnet ' \'. Brug af denne afgrænsningsstreng gør det muligt at bruge tegnet ' )' i bogstaver i rå strenge. For eksempel R"delimiter((a-z))delimiter"svarer det til "(a-z)"[3] .

"Rå" strengliteraler kan kombineres med et udvidet sæt literal (præfiks L"") eller et hvilket som helst Unicode-literalpræfiks.

LR"(Rå bred streng bogstavelig \t (uden fane))" u8R"XXX(Jeg er en "rå UTF-8"-streng.)XXX" uR"*(Dette er en "rå UTF-16"-streng.)*" UR"(Dette er en "rå UTF-32"-streng.)" Brugerdefinerede bogstaver

Brugerdefinerede bogstaver implementeres ved hjælp af operatøroverbelastning operator"". Bogstaver kan være inline eller constexpr qualifiers . Det er ønskeligt, at det bogstavelige begynder med en understregningstegn, da der kan være en konflikt med fremtidige standarder. For eksempel hører det bogstavelige i allerede til de komplekse tal fra std::complex.

Bogstaver kan kun tage én af følgende typer: const char * , unsigned long long int , long double , char , wchar_t , char16_t , char32_t. Det er nok kun at overbelaste det bogstavelige for typen const char * . Hvis der ikke findes en mere egnet kandidat, vil en operatør med den type blive tilkaldt. Et eksempel på konvertering af miles til kilometer:

constexpr int operator "" _mi ( usigned long long int i ) { returner 1,6 * i ;}

Streng bogstaver tager et andet argument std::size_tog et af de første: const char * , const wchar_t *, const char16_t * , const char32_t *. Streng bogstaver gælder for indgange omgivet af dobbelte anførselstegn.

Multithreaded memory model

C++11 standardiserer understøttelse af multi-threaded programmering. Der er to dele involveret: en hukommelsesmodel, der tillader flere tråde at eksistere side om side i et program, og et bibliotek, der understøtter kommunikation mellem tråde.

Hukommelsesmodellen definerer, hvordan flere tråde kan få adgang til den samme hukommelsesplacering og definerer, hvornår ændringer foretaget af en tråd bliver synlige for andre tråde.

Lagring med tråde Eksplicit standard og fjernelse af specielle metoder

Specifikatorer defaultog deletekan angives i stedet for metodeteksten.

klasse Foo { offentligt : foo () = standard ; Foo ( int x ) { /* ... */ } };

Specifikationen defaultbetyder standardimplementeringen og kan kun anvendes på specielle medlemsfunktioner:

  • standard konstruktør;
  • kopi konstruktør;
  • flytte konstruktør;
  • opgave operatør;
  • flytte operatør;
  • ødelægger.

Specifikationen deletemarkerer de metoder, der ikke kan arbejdes med. Tidligere var du nødt til at erklære sådanne konstruktører i klassens private omfang.

klasse Foo { offentligt : foo () = standard ; Foo ( const Foo & ) = slette ; void bar ( int ) = slet ; ugyldig streg ( dobbelt ) {} }; // ... Foo obj ; obj . stang ( 5 ); // fejl! obj . bar ( 5,42 ); // Okay Skriv long long int

Heltalstypen long long inter specificeret i C99 og er meget udbredt de facto i C++. Nu er det officielt inkluderet i standarden.

Statisk diagnostik

C++11 har to statiske diagnostiske mekanismer:

  • Nøgleordet static_assertgiver en kompileringsfejl, hvis udtrykket i parentes er falsk.
  • Et bibliotek type_traits, der indeholder skabeloner, der giver typeoplysninger på kompileringstidspunktet.
#include <type_træk> skabelon < classT > _ void run ( T * aData , size_t n ) { static_assert ( std :: is_pod < T >:: value , "Typen T skal være enkel." ); ... } Arbejde med størrelsen af ​​datamedlemmer i klasser uden at oprette et objekt

C++03 tillod operatøren sizeofat blive brugt på simple typer og objekter. Men følgende konstruktion var ugyldig:

struct SomeType { OtherType member ; }; sizeof ( SomeType :: medlem ); //Virker ikke i C++03, men sandt i C++11.

Resultatet af dette opkald skal være en størrelse OtherType. C++03 understøtter ikke et sådant opkald, og denne kode vil ikke kompilere. C++11 tillader sådanne konstruktioner.

Objektjusteringskontrol og -justeringsanmodninger

C++11 giver dig mulighed for at justere variabler ved hjælp af operatorerne alignofog alignas.

alignoftager en type og returnerer antallet af bytes, som objektet kan flyttes med. For eksempel struct X { int n; char c; };vil den for 8 bytes alignofreturnere værdien 4. For links returnerer den værdien for linktypen; for arrays, værdien for array-elementet

alignasstyrer justeringen af ​​et objekt i hukommelsen. For eksempel kan du angive, at et char-array skal være korrekt justeret for at gemme typen float:

alignas ( float ) usigneret char c [ sizeof ( float )] Tillad implementeringer med en skraldeopsamler Attributter

Ændringer til C++ Standardbiblioteket

Ændringer af eksisterende komponenter

  • Når den er indsat i, std::setved programmøren nogle gange, hvilken position det nye element ender i. Til dette bruges en valgfri parameter - "hint"; hvis gættet er korrekt, vil tidsestimatet være en amortiseret konstant, ikke O(log n) . Betydningen af ​​"hint" i C++11 blev ændret: tidligere betød det elementet før det nuværende, hvilket ikke er helt korrekt: det er ikke klart, hvad man skal gøre, hvis indsættelsen er i den første position. Nu er dette elementet efter det nuværende.
  • Der er skrevet en praktisk skabelon, der kalder konstruktører uden hukommelsesallokering - std::allocator_traits<>::construct(). En metode er blevet tilføjet til alle containere emplace, der skaber et objekt på plads.
  • Tilføjet nye C++11 sprogfunktioner.
  • Tilføjede metoder cbeginog cendgaranteret at skabe konst iteratorer. Praktisk til metaprogrammering, til indstillingstyper via auto.
  • I beholdere, der starter hukommelsen med en margen, er der dukket en funktion op shrink_to_fit.
  • B std::listsætter strengere grænser for, hvad der gøres i O ( n ), og hvad der gøres i konstant tid.
  • Tilføjet std::vectordirekte hukommelsesadgang via data().
  • Forbyd flere std::stringat henvise til samme hukommelse. Takket være dette dukkede direkte adgang gennem front(), hvilket er praktisk, for eksempel for interaktionen af ​​streng og WinAPI , op .

Flow kontrol

Mens C++03-sproget giver en hukommelsesmodel, der understøtter multithreading, er hovedstøtten til faktisk brug af multithreading leveret af C++11-standardbiblioteket.

En trådklasse ( std::thread) er tilvejebragt, der accepterer et funktionsobjekt (og en valgfri liste over argumenter, der skal overføres til det) for at køre på en ny tråd. Du kan tvinge en tråd til at stoppe, før en anden eksekverende tråd er afsluttet, ved at understøtte trådpooling gennem en medlemsfunktion std::thread::join(). Hvis det er muligt, er adgang til trådens oprindelige håndtag tilvejebragt for platformsspecifikke operationer via medlemsfunktionen std::thread::native_handle().

Til synkronisering mellem tråde tilføjes passende mutexes ( std::mutex, std::recursive_mutexosv.) og betingelsesvariabler ( std::condition_variableog std::condition_variable_any) til biblioteket. De er tilgængelige via ressourceinitialisering (RAII) låse ( std::lock_guardog std::unique_lock) og låsealgoritmer for nem brug.

Højtydende arbejde på lavt niveau kræver nogle gange kommunikation mellem tråde uden overhead af mutexes. Dette gøres ved hjælp af atomoperationer på hukommelsessteder. De kan eventuelt angive de minimumsgrænser for hukommelsessynlighed, der kræves for operationen. Eksplicitte hukommelsesbarrierer kan også bruges til dette formål.

C++11-trådbiblioteket inkluderer også futures og løfter om at overføre asynkrone resultater mellem tråde og en klasse std::packaged_tasktil at ombryde et funktionskald, der kan generere et sådant asynkront resultat. Futuresforslaget er blevet kritiseret, da det mangler en måde at kombinere futures på og kontrollere opfyldelsen af ​​et enkelt løfte i et sæt løfter.

Yderligere gevindfaciliteter på højt niveau, såsom trådpuljer, er blevet placeret i en fremtidig C++ hvidbog. De er ikke en del af C++11, men deres endelige implementering forventes at blive bygget helt oven på trådbibliotekets funktioner.

Den nye funktion std::asyncgiver en bekvem måde at køre opgaver på og binde resultatet af deres udførelse til et objekt i std::future. Brugeren kan vælge, om jobbet skal køre asynkront på en separat tråd eller synkront på den aktuelle tråd, der venter på værdien.

Hash-tabeller

std::hash_setog std::hash_maphar længe været en ikke-standard STL-udvidelse, faktisk implementeret i de fleste compilere. I C++11 blev de standard, under navnene std::unordered_setog std::unordered_map. Selvom de i virkeligheden er hash-tabeller, og standarden ikke efterlader meget slingreplads, er navnene givet i C++-stil: ikke "hvordan de implementeres", men "hvad de er".

Regulære udtryk

Det nye bibliotek, erklæret i header-filen <regex>, indeholder flere nye klasser:

  • Regulære udtryk er repræsenteret som forekomster af std::regex;
  • søgeresultater er repræsenteret som skabelonforekomster std::match_results.

Funktionen std::regex_searchbruges til at søge, til 'find og erstat' operationen bruges funktionen std::regex_replace. Funktionen returnerer en streng efter at have udført udskiftningen. Algoritmerne std::regex_searchog std::regex_replacetager et regulært udtryk og en streng som input og returnerer de fundne resultater som en forekomst af std::match_results.

Eksempel på brug std::match_results:

const char * reg_esp = "[ ,. \\ t \\ n;:]" ; // Liste over skilletegn. // det samme kan gøres ved at bruge "rå" strenge: // const char *reg_esp = R"([ ,.\t\n;:])"; std :: regex rgx ( reg_esp ); // 'regex' er en instans af skabelonklassen // 'basic_regex' med skabelonparameteren 'char'. std :: cmatch match ; // 'cmatch' er en forekomst af skabelonklassen // 'match_results' med skabelonparameteren 'const char *'. const char * target = "Uset universitet - Ankh-Morpork" ; // Retter alle ord i strengen 'target' adskilt af tegn fra 'reg_esp'. if ( std :: regex_search ( mål , match , rgx ) ) { // Hvis ordene adskilt af de givne tegn er til stede i strengen. const size_t n = match . størrelse (); for ( størrelse_t a = 0 ; a < n ; a ++ ) { std :: string str ( match [ a ]. først , match [ a ]. sekund ); std :: cout << str << " \n " ; } }

Bemærk, at dobbelte omvendte skråstreg er påkrævet, fordi C++ bruger omvendte skråstreg til at undslippe tegn. Du kan bruge "rå strenge" - endnu en nyskabelse af C++11-standarden.

Biblioteket <regex>kræver ingen ændring af eksisterende header-filer eller installation af yderligere sprogudvidelser.


Udvidelige tilfældige talgenereringsklasser

C-standardbiblioteket tillod generering af pseudo-tilfældige tal ved hjælp af rand. Dens adfærd kan dog variere afhængigt af implementeringen.

Denne funktionalitet er opdelt i to dele: generatormotoren, som indeholder den aktuelle tilstand af tilfældig talgeneratoren og producerer pseudo-tilfældige tal, og fordelingen, som bestemmer rækkevidden og den matematiske fordeling af resultatet. Kombinationen af ​​disse to objekter skaber en tilfældig talgenerator.

Generatormotorer:

Distributioner:

Eksempel:

#inkluder <tilfældig> #include <funktionel> std :: uniform_int_distribution < int > distribution ( 0 , 99 ); std :: mt19937motor ; _ // Mersenne vortex MT19937 auto generator = std :: bind ( distribution , motor ); int random = generator (); // Få et tilfældigt tal mellem 0 og 99. int random2 = distribution ( engine ); // Få et tilfældigt tal ved at bruge motoren og distributionen direkte.



Planlagte funktioner er ikke inkluderet i standarden

Moduler Den enorme mængde af header-filer førte til en kvadratisk stigning i kompileringstiden: både mængden af ​​kode og antallet af moduler i en enkelt kompileringsenhed stiger. Moduler bør give en mekanisme svarende til Delphi DCU filer eller Java klasse filer .

Fjernede eller forældede funktioner

Se også

Noter

  1. Herb Sutter , Vi har en international standard: C++0x er enstemmigt godkendt Arkiveret 11. december 2018 på Wayback Machine
  2. Scott Meyers , Resumé af C++11-funktionernes tilgængelighed i gcc og MSVC Arkiveret 26. oktober 2011 på Wayback Machine , 16. august 2011
  3. ISO , ISO/IEC 14882:2011 Arkiveret 29. januar 2013 på Wayback Machine
  4. C++0x navn defineret i endeligt udkast til N3290 Arkiveret 20. juni 2010 på Wayback Machine
  5. Stroustrup, Bjørn  - C++0x - den næste ISO C++ standard Arkiveret 11. maj 2011 på Wayback Machine
  6. C++ Standards Committee Papers . Hentet 24. februar 2008. Arkiveret fra originalen 18. marts 2010.
  7. C++ - kilden Bjarne Stroustrup ( 2. januar 2006 ) Et kort kig på C++0x . (Engelsk)

Dokumenter fra C++ Standards Committee

  •   Dok.nr. 1401: Jan Kristoffersen (21. oktober 2002)Atomoperationer med flertrådede miljøer
  •   Dok.nr. 1402: Doug Gregor (22. oktober 2002)Et forslag om at tilføje en polymorf funktionsobjektomslag til standardbiblioteket
  •   Dok.nr. 1403: Doug Gregor (8. november 2002)Forslag om tilføjelse af tupletyper til standardbiblioteket
  •   Dok.nr. 1424: John Maddock (3. marts 2003)Et forslag om at tilføje typetræk til standardbiblioteket
  •   Dok.nr. 1429: John Maddock (3. marts 2003)Et forslag om at tilføje regulært udtryk til standardbiblioteket
  •   Dok.nr. 1449: B. Stroustrup, G. Dos Reis, Mat Marcus, Walter E. Brown, Herb Sutter (7. april 2003)Forslag om at tilføje skabelonaliaser til C++
  •   Dok.nr. 1450: P. Dimov, B. Dawes, G. Colvin (27. marts 2003)Et forslag om at føje smarte pointere til generelle formål til bibliotekets tekniske rapport (revision 1)
  •   Dok.nr. 1452: Jens Maurer (10. april 2003)Et forslag om at tilføje en udvidelig tilfældig nummerfacilitet til standardbiblioteket (revision 2)
  •   Dok.nr. 1453: D. Gregor, P. Dimov (9. april 2003)Et forslag om at tilføje en referenceindpakning til standardbiblioteket (revision 1)
  •   Dok.nr. 1454: Douglas Gregor, P. Dimov (9. april 2003)En ensartet metode til beregning af funktionsobjektreturtyper (revision 1)
  •   Dok.nr. 1456: Matthew Austern (9. april 2003)Et forslag om at tilføje hashtabeller til standardbiblioteket (revision 4)
  •   Dok.nr. 1471: Daveed Vandevoorde (18. april 2003)Reflekterende metaprogrammering i C++
  •   Dok.nr. 1676: Bronek Kozicki (9. september 2004)Ikke-medlem overbelastet kopiopgaveoperatør
  •   Dok.nr. 1704: Douglas Gregor, Jaakko Järvi, Gary Powell (10. september 2004)Variadic Templates: Exploring the Design Space
  •   Dok.nr. 1705: J. Järvi, B. Stroustrup, D. Gregor, J. Siek, G. Dos Reis (12. september 2004)Decltype (og auto)
  •   Dok.nr. 1717: Francis Glassborow, Lois Goldthwaite (5. november 2004)eksplicitte klasse- og standarddefinitioner
  •   Dok.nr. 1719: Herb Sutter, David E. Miller (21. oktober 2004)Stærkt indskrevne enums (revision 1)
  •   Dok.nr. 1720: R. Klarer, J. Maddock, B. Dawes, H. Hinnant (20. oktober 2004)Forslag om at tilføje statiske påstande til kernesproget (revision 3)
  •   Dok.nr. 1757: Daveed Vandevoorde (14. januar 2005)Right Angle Brackets (Revision 2)
  •   Dok.nr. 1811: J. Stephen Adamczyk (29. april 2005)Tilføjelse af den lange lange type til C++ (revision 3)
  •   Dok.nr. 1815: Lawrence Crowl (2. maj 2005)ISO C++ Strategisk plan for multithreading
  •   Dok.nr. 1827: Chris Uzdavinis, Alisdair Meredith (29. august 2005)An Explicit Override Syntax for C++
  •   Dok.nr. 1834: Detlef Vollmann (24. juni 2005)En bøn om rimelig parallel behandlingsstøtte i C++
  •   Dok.nr. 1836: ISO/IEC DTR 19768 (24. juni 2005)Udkast til teknisk rapport om C++ biblioteksudvidelser
  •   Dok.nr. 1886: Gabriel Dos Reis, Bjarne Stroustrup (20. oktober 2005)Specificering af C++ begreber
  •   Dok.nr. 1891: Walter E. Brown (18. oktober 2005)Fremskridt mod Opaque Typedefs for C++0X
  •   Dok.nr. 1898: Michel Michaud, Michael Wong (6. oktober 2004)Speditører og nedarvede konstruktører
  •   Dok.nr. 1919: Bjarne Stroustrup, Gabriel Dos Reis (11. december 2005)Initialiseringslister
  •   Dok.nr. 1968: V Samko J Willcock, J Järvi, D Gregor, A Lumsdaine (26. februar 2006)Lambda-udtryk og lukninger for C++
  •   Dok.nr. 1986: Herb Sutter, Francis Glassborow (6. april 2006)Delegerende konstruktører (revision 3)
  •   Dok.nr. 2016: Hans Boehm, Nick Maclaren (21. april 2002)Should volatile Acquire Atomicity and Thread Visibility Semantics?
  •   Dok.nr. 2142: ISO/IEC DTR 19768 (12. januar 2007)Status for C++ Evolution (mellem Portland og Oxford 2007-møder)
  •   Dok.nr. 2228: ISO/IEC DTR 19768 (3. maj 2007)Status for C++ Evolution (Oxford 2007 Meetings)
  •   Dok.nr. 2258: G. Dos Reis og B. StroustrupSkabeloner Aliaser
  •   Dok.nr. 2280: Lawrence Crowl (2. maj 2007)Thread-Local Storage
  •   Dok.nr. 2291: ISO/IEC DTR 19768 (25. juni 2007)Status for C++ Evolution (Toronto 2007 Meetings)
  •   Dok.nr. 2336: ISO/IEC DTR 19768 (29. juli 2007)Status for C++ Evolution (Toronto 2007 Meetings)
  •   Dok.nr. 2389: ISO/IEC DTR 19768 (7. august 2007)Status for C++ Evolution (før Kona 2007-møder)
  •   Dok.nr. 2431: SC22/WG21/N2431 = J16/07-0301 (2. oktober 2007),Et navn til nul-markøren: nullptr
  •   Dok.nr. 2432: ISO/IEC DTR 19768 (23. oktober 2007)Status for C++ Evolution (post-Kona 2007-møde)
  •   Dok.nr. 2437: Lois Goldthwaite (5. oktober 2007)Eksplicitte konverteringsoperatører
  •   Dok.nr. 2461: ISO/IEC DTR 19768 (22. oktober 2007)Working Draft, Standard for programmeringssprog C++
  •   Dok.nr. 2507: ISO/IEC DTR 19768 (4. februar 2008)Status for C++ Evolution (før-Bellevue 2008-møde)
  •   Dok.nr. 2544: Alan Talbot, Lois Goldthwaite, Lawrence Crowl, Jens Maurer (29. februar 2008)Ubegrænsede fagforeninger
  •   Dok.nr. 2565: ISO/IEC DTR 19768 (7. marts 2008)Status for C++ Evolution (post-Bellevue 2008-møde)
  •   Dok.nr. 2597: ISO/IEC DTR 19768 (29. april 2008)Status for C++ Evolution (før-Antipolis 2008-møde)
  •   Dok.nr. 2606: ISO/IEC DTR 19768 (19. maj 2008)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 2697: ISO/IEC DTR 19768 (15. juni 2008)Referat af WG21-møde 8.-15. juni 2008
  •   Dok.nr. 2798: ISO/IEC DTR 19768 (4. oktober 2008)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 2857: ISO/IEC DTR 19768 (23. marts 2009)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 2869: ISO/IEC DTR 19768 (28. april 2009)Status for C++ Evolution (post-San Francisco 2008-møde)
  •   Dok.nr. 3000: ISO/ISC DTR 19769 (9. november 2009)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3014: Stephen D. Clamage (4. november 2009)DAGSORDEN, PL22.16 Møde nr. 53, WG21 Møde nr. 48, 8.-13. marts 2010, Pittsburgh, PA
  •   Dok.nr. 3082: Herb Sutter (13. marts 2010)C++0x mødeplan
  •   Dok.nr. 3092: ISO/ISC DTR 19769 (26. marts 2010)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3126: ISO/ISC DTR 19769 (21. august 2010)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3225: ISO/ISC DTR 19769 (27. november 2010)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3242: ISO/ISC DTR 19769 (28. februar 2011)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3291: ISO/ISC DTR 19769 (5. april 2011)Arbejdsudkast, standard for programmeringssprog C++
  •   Dok.nr. 3290: ISO/ISC DTR 19769 (5. april 2011)FDIS, standard for programmeringssprog C++
  •   Dok.nr. 3337 : Dato: 2012-01-16 Arbejdsudkast, standard for programmeringssprog C++

Links

Litteratur

  • Stanley B. Lippman, Josy Lajoye, Barbara E. Moo. C++ programmeringssprog. Core Course 5th Edition = C++ Primer (5th Edition). - M. : "Williams" , 2014. - 1120 s. - ISBN 978-5-8459-1839-0 .
  • Siddhartha Rao. Lær dig selv C++ på 21 dage, 7. udgave = Sams Lær dig selv C++ på én time om dagen, 7. udgave. - M. : "Williams" , 2013. - 688 s. — ISBN 978-5-8459-1825-3 .
  • Stephen Prata. C++ programmeringssprog (C++11). Forelæsninger og øvelser, 6. udgave = C++ Primer Plus, 6. udgave (udviklerbibliotek). - M. : "Williams" , 2012. - 1248 s. - ISBN 978-5-8459-1778-2 .