C++11

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 15 september 2020; kontroller kräver 24 redigeringar .

C++11 [1] [2] eller ISO/IEC 14882:2011 [3] (i arbetet med standarden hade den kodnamnet C++0x [4] [5] ) — en ny version av språkstandarden C++ istället för den tidigare giltiga ISO /IEC 14882:2003. Den nya standarden innehåller tillägg till kärnan i språket och en utökning av standardbiblioteket, inklusive det mesta av TR1  - utom kanske biblioteket med speciella matematiska funktioner. Nya versioner av standarderna, tillsammans med några andra C++-standardiseringsdokument, publiceras på ISO C++-kommitténs webbplats [6] . C++ programmeringsexempel

Programmeringsspråk genomgår en gradvis utveckling av sina möjligheter (för tillfället, efter C++11, har följande standardtillägg publicerats: C++14, C++17, C++20). Denna process orsakar oundvikligen kompatibilitetsproblem med befintlig kod. Appendix C.2 [diff.cpp03] i Final Draft International Standard N3290 beskriver några av  inkompatibiliteterna mellan C++11 och C++03.

Föreslagna ändringar av standarden

Som redan nämnts kommer ändringarna att påverka både C++-kärnan och dess standardbibliotek.

Vid utvecklingen av varje avsnitt av den framtida standarden använde kommittén ett antal regler:

Uppmärksamhet ägnas åt nybörjare, som alltid kommer att utgöra majoriteten av programmerare. Många nybörjare försöker inte fördjupa sina kunskaper om C++, utan begränsar sig till att använda det när de arbetar med snäva specifika uppgifter [7] . Dessutom, med tanke på mångsidigheten hos C++ och dess användningsbredd (inklusive både mångfalden av applikationer och programmeringsstilar), kan även proffs hitta sig själva som nya i nya programmeringsparadigm .

Utöka kärnspråket

Kommitténs primära uppgift är att utveckla kärnan i C++-språket. Kärnan har förbättrats avsevärt, stöd för flera trådar har lagts till, stöd för generisk programmering har förbättrats , initiering har förenats och arbete har gjorts för att förbättra dess prestanda.

För enkelhetens skull är kärnans funktioner och ändringar uppdelade i tre huvuddelar: prestandaförbättringar, bekvämlighetsförbättringar och ny funktionalitet. Individuella element kan tillhöra flera grupper, men kommer endast att beskrivas i en - den mest lämpliga.

Prestandaförbättring

Dessa språkkomponenter introduceras för att minska minneskostnader eller förbättra prestanda.

Tillfälliga objektreferenser och flytta semantik

Enligt C++-standarden kan ett temporärt objekt som är ett resultat av utvärderingen av ett uttryck skickas till funktioner, men endast genom en konstant referens ( const & ). Funktionen kan inte avgöra om det skickade objektet kan anses vara temporärt och modifierbart (ett const-objekt som också kan skickas av en sådan referens kan inte modifieras (lagligt)). Detta är inte ett problem för enkla strukturer som complex, men för komplexa typer som kräver minnesallokering-deallokering kan det vara tidskrävande att förstöra ett temporärt objekt och skapa ett permanent, medan man helt enkelt kan skicka pekare direkt.

C++11 introducerar en ny typ av referens , rvalue - referensen .  Dess deklaration är: typ && . Nya regler för överbelastningsupplösning tillåter dig att använda olika överbelastade funktioner för icke-konst temporära objekt, betecknade med rvärden, och för alla andra objekt. Denna innovation möjliggör implementering av den så kallade rörelsesemantiken .

Till exempel std::vector är ett enkelt omslag runt en C-array och en variabel som lagrar dess storlek. Kopieringskonstruktören std::vector::vector(const vector &x)kommer att skapa en ny array och kopiera informationen; överföringskonstruktorn std::vector::vector(vector &&x)kan helt enkelt utbyta pekare och variabler som innehåller längden.

Annonsexempel.

mall < klass T > klassvektor _ { vektor ( konst vektor & ); // Kopiera konstruktor (långsam) vektor ( vektor && ); // Överför konstruktor från ett temporärt objekt (snabb) vektor & operator = ( const vektor & ); // Regelbunden tilldelning (långsam) vektor & operator = ( vektor && ); // Flytta tillfälligt objekt (snabbt) void foo () & ; // Funktion som bara fungerar på ett namngivet objekt (långsamt) void foo () && ; // Funktion som bara fungerar för ett tillfälligt objekt (snabbt) };

Det finns flera mönster förknippade med tillfälliga länkar, varav de två viktigaste är och . Den första gör ett vanligt namngivet objekt till en tillfällig referens: moveforward

// std::move mall exempel void bar ( std :: string && x ) { statisk std :: stringsomeString ; _ someString = std :: flytta ( x ); // inuti funktionen x=string&, därav det andra draget för att anropa dragtilldelningen } std :: trådig ; _ bar ( std :: flytta ( y )); // första drag förvandlar sträng& till sträng&& till anropsfältet

Mallen används endast i metaprogrammering, kräver en explicit mallparameter (den har två oskiljbara överbelastningar) och är associerad med två nya C++-mekanismer. Den första är länklimning: , sedan . För det andra kräver funktionen bar() ovan ett temporärt objekt på utsidan, men på insidan är x-parametern ett ordinärt namn (lvalue) för reserv, vilket gör det omöjligt att automatiskt skilja parametern string& från parametern string&&. I en vanlig icke-mallfunktion kan programmeraren sätta move(), men hur är det med mallen? forwardusing One=int&&; using Two=One&;Two=int&

// exempel på att använda mallen std::forward class Obj { std :: stringfield ; _ mall < classT > _ Obj ( T && x ) : fält ( std :: framåt < T > ( x )) {} };

Denna konstruktor täcker de vanliga (T=sträng&), kopierings- (T=const sträng&) och flytta (T=sträng) överbelastningar med referenslimning. Och framåt gör ingenting eller expanderar till std::move beroende på typen av T, och konstruktorn kommer att kopiera om det är en kopia och flytta om det är ett drag.

Generiska konstantuttryck

C++ har alltid haft konceptet med konstanta uttryck. Således gav uttryck som 3+4 alltid samma resultat utan att orsaka några biverkningar. I sig själva ger konstanta uttryck ett bekvämt sätt för C++-kompilatorer att optimera resultatet av kompileringen. Kompilatorer utvärderar resultaten av sådana uttryck endast vid kompileringstid och lagrar de redan beräknade resultaten i programmet. Således utvärderas sådana uttryck endast en gång. Det finns också ett fåtal fall där språkstandarden kräver användning av konstanta uttryck. Sådana fall kan till exempel vara definitioner av externa arrayer eller enumvärden.


int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // skapa en array med 12 heltal; förbjudet i C++

Ovanstående kod är olaglig i C++ eftersom GiveFive() + 7 inte tekniskt sett är ett konstant uttryck känt vid kompileringstillfället. Kompilatorn vet helt enkelt inte vid den tidpunkten att funktionen faktiskt returnerar en konstant vid körning. Anledningen till detta kompilatorresonemang är att den här funktionen kan påverka tillståndet för en global variabel, anropa en annan icke-konst körtidsfunktion och så vidare.

C++11 introducerar nyckelordet constexpr , som låter användaren säkerställa att antingen en funktion eller en objektkonstruktor returnerar en kompileringstidskonstant. Koden ovan kan skrivas om så här:

constexpr int GiveFive () { return 5 ;} int some_value [ GiveFive () + 7 ]; // skapa en array med 12 heltal; tillåtet i C++11

Detta nyckelord låter kompilatorn förstå och verifiera att GiveFive returnerar en konstant.

Användningen av constexpr sätter mycket strikta begränsningar för funktionernas åtgärder:

  1. en sådan funktion måste returnera ett värde;
  2. funktionens kropp måste ha formen returuttryck ;
  3. uttrycket måste bestå av konstanter och/eller anrop till andra constexpr- funktioner;
  4. en funktion som betecknas med constexpr kan inte användas förrän den har definierats i den aktuella kompileringsenheten.

I den tidigare versionen av standarden kunde endast heltals- eller enumtypvariabler användas i konstanta uttryck. I C++11 upphävs denna begränsning för variabler vars definition föregås av nyckelordet constexpr:

constexpr dubbel accelerationOfGravity = 9,8 ; constexpr dubbelmåneGravity = accelerationOfGravity / 6 ; _

Sådana variabler anses redan implicit betecknas av nyckelordet const . De kan endast innehålla resultaten av konstanta uttryck eller konstruktörerna av sådana uttryck.

Om det är nödvändigt att konstruera konstanta värden från användardefinierade typer, kan konstruktörer av sådana typer också deklareras med constexpr . En konstantuttryckskonstruktor, liksom konstantfunktioner, måste också definieras innan den används för första gången i den aktuella kompileringsenheten. En sådan konstruktör måste ha en tom kropp, och en sådan konstruktor måste initiera medlemmarna av sin typ med endast konstanter.

Ändringar i definitionen av enkla data

I standard C++ kan endast strukturer som uppfyller en viss uppsättning regler betraktas som en vanlig datatyp ( POD). Det finns goda skäl att förvänta sig att dessa regler utökas så att fler typer betraktas som POD:er. Typer som uppfyller dessa regler kan användas i en C-kompatibel objektlagerimplementering, men C++03s lista över dessa regler är alltför restriktiv.

C++11 kommer att lätta på flera regler angående definitionen av enkla datatyper.

En klass anses vara en enkel datatyp om den är trivial , har en standardlayout ( standardlayout ) och om typerna av alla dess icke-statiska datamedlemmar också är enkla datatyper.

En trivial klass är en klass som:

  1. innehåller en trivial standardkonstruktor,
  2. innehåller inte icke-triviala kopiekonstruktorer,
  3. innehåller inte icke-triviala rörelsekonstruktorer,
  4. innehåller inte icke-triviala operatörer för kopieringsuppdrag,
  5. innehåller inte icke-triviala rörelseuppdragsoperatörer,
  6. innehåller en trivial förstörare.

En klass med standardplacering är en klass som:

  1. innehåller inte icke-statiska datamedlemmar av en specialplacerad klasstyp (eller en uppsättning element av den typen) eller en referenstyp,
  2. innehåller inte virtuella funktioner,
  3. innehåller inte virtuella basklasser,
  4. har samma tillgänglighetstyp ( public, private, protected) för alla icke-statiska datamedlemmar,
  5. har inte basklasser med icke-standardplacering,
  6. är inte en klass som samtidigt innehåller ärvda och icke-ärvda icke-statiska datamedlemmar, eller innehåller icke-statiska datamedlemmar som ärvts från flera basklasser samtidigt,
  7. har inte basklasser av samma typ som den första icke-statiska datamedlemmen (om någon).

Snabba upp kompileringen

Externa mallar

I standard C++ måste kompilatorn instansiera en mall när den stöter på sin fulla specialisering i en översättningsenhet. Detta kan avsevärt öka kompileringstiden, särskilt när mallen instansieras med samma parametrar i ett stort antal översättningsenheter. Det finns för närvarande inget sätt att berätta för C++ att det inte ska finnas någon instansiering.

C++11 introducerade idén med externa mallar. C++ har redan en syntax för att tala om för kompilatorn att en mall ska instansieras vid en viss punkt:

mallklass std :: vektor < MyClass > ; _

C++ saknar förmågan att förhindra kompilatorn från att instansiera en mall i en översättningsenhet. C++11 utökar helt enkelt denna syntax:

extern mallklass std :: vektor < MyClass > ; _

Detta uttryck säger åt kompilatorn att inte instansiera mallen i denna översättningsenhet.

Förbättrad användbarhet

Dessa funktioner är avsedda att göra språket lättare att använda. De låter dig stärka typsäkerheten, minimera kodduplicering, göra det svårare för kod att missbrukas och så vidare.

Initialiseringslistor

Konceptet med initialiseringslistor kom till C++ från C. Tanken är att en struktur eller array kan skapas genom att skicka en lista med argument i samma ordning som strukturens medlemmar definieras. Initialiseringslistor är rekursiva, vilket gör att de kan användas för arrayer av strukturer och strukturer som innehåller kapslade strukturer.

struct objekt { flyta först ; int andra ; }; Objekt skalär = { 0.43f , 10 }; // ett objekt, med first=0.43f och second=10 Object anArray [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // array av tre objekt

Initialiseringslistor är mycket användbara för statiska listor och när du vill initiera en struktur till ett specifikt värde. C++ innehåller också konstruktorer, som kan innehålla det allmänna arbetet med att initiera objekt. C++-standarden tillåter användning av initialiseringslistor för strukturer och klasser, förutsatt att de överensstämmer med POD-definitionen (Plain Old Data). Icke-POD-klasser kan inte använda initialiseringslistor för initiering, inklusive standard C++-behållare som vektorer.

C++11 har associerat konceptet med initialiseringslistor och en mallklass som heter std::initializer_list . Detta gjorde det möjligt för konstruktörer och andra funktioner att ta emot initialiseringslistor som parametrar. Till exempel:

klass SequenceClass { offentliga : SequenceClass ( std :: initializer_list < int > list ); };

Den här beskrivningen låter dig skapa en SequenceClass från en sekvens av heltal enligt följande:

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

Detta visar hur en speciell typ av konstruktör fungerar för en initialiseringslista. Klasser som innehåller sådana konstruktorer behandlas på ett speciellt sätt under initiering (se nedan ).

Klassen std::initializer_list<> är definierad i standardbiblioteket C++11. Objekt av den här klassen kan dock endast skapas statiskt av C++11-kompilatorn med hjälp av syntaxen {}. Listan kan kopieras efter att den skapats, men detta kommer att kopieras för referens. Initieringslistan är const: varken dess medlemmar eller deras data kan ändras efter skapandet.

Eftersom std::initializer_list<> är en fullfjädrad typ, kan den användas i mer än bara konstruktörer. Vanliga funktioner kan ta inskrivna initialiseringslistor som ett argument, till exempel:

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

Standardbehållare kan initieras så här:

std :: vektor < std :: sträng > v = { "xyzzy" , "plugh" , "abracadabra" }; std :: vektor < std :: sträng > v { "xyzzy" , "plugh" , "abracadabra" }; Generisk initiering

C++-standarden innehåller ett antal problem relaterade till typinitiering. Det finns flera sätt att initiera typer, och alla leder inte till samma resultat. Till exempel kan den traditionella syntaxen för en initierande konstruktor se ut som en funktionsdeklaration, och extra försiktighet måste iakttas för att förhindra att kompilatorn tolkar den fel. Endast aggregattyper och POD-typer kan initieras med aggregatinitierare (av typen SomeType var = {/*stuff*/};).

C++11 tillhandahåller en syntax som gör att en enda form av initiering kan användas för alla typer av objekt genom att utöka initieringslistans syntax:

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

Att initiera var1 fungerar precis som att initiera aggregat, det vill säga att varje objekt initieras genom att kopiera motsvarande värde från initieringslistan. Vid behov kommer implicit typkonvertering att tillämpas. Om den önskade transformationen inte existerar kommer källkoden att betraktas som ogiltig. Under initieringen av var2 kommer konstruktorn att anropas.

Det är möjligt att skriva kod så här:

struktur IdString { std :: strängnamn ; _ int identifierare ; }; IdString GetString () { returnera { "SomeName" , 4 }; // Observera bristen på explicita typer }

Generisk initiering ersätter inte helt syntax för konstruktorinitiering. Om en klass har en konstruktor som tar en initialiseringslista ( TypeName(initializer_list<SomeType>); ) som argument, kommer den att ha företräde framför andra alternativ för att skapa objekt. Till exempel, i C++11 innehåller std::vector en konstruktor som tar en initialiseringslista som ett argument:

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

Den här koden kommer att resultera i ett konstruktoranrop som tar en initialiseringslista som ett argument, snarare än en enparameters konstruktor som skapar en behållare av den givna storleken. För att anropa denna konstruktor måste användaren använda standardsyntaxen för konstruktoranrop.

Skriv inferens

I standard C++ (och C) måste typen av en variabel anges uttryckligen. Men med tillkomsten av malltyper och mallmetaprogrammeringstekniker kan typen av vissa värden, särskilt funktionsreturvärden, inte enkelt specificeras. Detta leder till svårigheter att lagra mellanliggande data i variabler, ibland kan det vara nödvändigt att känna till den interna strukturen för ett visst metaprogrammeringsbibliotek.

C++11 erbjuder två sätt att lindra dessa problem. För det första kan definitionen av en explicit initierbar variabel innehålla nyckelordet auto . Detta kommer att resultera i skapandet av en variabel av typen av initialiseringsvärdet:

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

Typen someStrangeCallableType blir den typ som den konkreta implementeringen av mallfunktionen returnerar std::bindför de givna argumenten. Denna typ kommer lätt att bestämmas av kompilatorn under semantisk analys, men programmeraren måste göra lite forskning för att bestämma typen.

Den andraVariabeltypen är också väldefinierad, men kan lika gärna definieras av programmeraren. Denna typ är int , samma som en heltalskonstant.

Dessutom kan nyckelordet decltype användas för att bestämma typen av ett uttryck vid kompileringstillfället . Till exempel:

int someInt ; decltype ( someInt ) otherIntegerVariable = 5 ;

Att använda decltype är mest användbart i samband med auto , eftersom typen av en variabel som deklareras som auto endast är känd för kompilatorn. Att använda decltype kan också vara ganska användbart i uttryck som använder operatoröverbelastning och mallspecialisering.

autokan också användas för att minska kodredundans. Till exempel, istället för:

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

programmeraren kan skriva:

för ( auto itr = myvec . cbegin (); itr != myvec . cend (); ++ itr )

Skillnaden blir särskilt märkbar när en programmerare använder ett stort antal olika behållare, även om det fortfarande finns ett bra sätt att minska redundant kod med typedef.

En typ som är markerad med decltype kan skilja sig från den typ som antas med auto .

#inkludera <vektor> int main () { const std :: vektor < int > v ( 1 ); auto a = v [ 0 ]; // typ a - int decltype ( v [ 0 ]) b = 1 ; // typ b - const int& (returvärde // std::vector<int>::operator[](size_type) const) auto c = 0 ; // skriv c - int auto d = c ; // typ d - int decltype ( c ) e ; // typ e - int, typ av enhet som heter c decltype (( c )) f = c ; // typ f är int& eftersom (c) är ett lvärde decltype ( 0 ) g ; // typ g är int eftersom 0 är ett rvärde } For-loop genom en samling

I standard C++ kräver det mycket kod att iterera över elementen i en samling . Vissa språk, som C# , har faciliteter som tillhandahåller en " foreach " -sats som automatiskt går igenom elementen i en samling från början till slut. C++11 introducerar en liknande anläggning. For -satsen gör det lättare att iterera över en samling element:

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

Denna form av för, som kallas "range-based for" på engelska, kommer att besöka varje del av samlingen. Detta kommer att gälla för C -matriser , initialiseringslistor och alla andra typer som har funktioner begin()och end()som returnerar iteratorer . Alla behållare i standardbiblioteket som har ett start/slut-par kommer att fungera med en for-sats på samlingen.

En sådan cykel kommer också att fungera, till exempel med C-liknande arrayer, eftersom C++11 introducerar på konstgjord väg de nödvändiga pseudometoderna för dem (början, slutet och några andra).

// områdesbaserad genomgång av den klassiska matrisen int arr1 [] = { 1 , 2 , 3 }; for ( auto el : arr1 ); Lambdafunktioner och uttryck

I standard C++, till exempel, när man använder standard C++ biblioteksalgoritmer sortera och hitta , finns det ofta ett behov av att definiera predikatfunktioner nära där algoritmen anropas. Det finns bara en mekanism i språket för detta: möjligheten att definiera en funktionsklass (att skicka en instans av en klass definierad inuti en funktion till algoritmer är förbjuden (Meyers, Effektiv STL)). Ofta är denna metod för överflödig och utförlig och gör det bara svårt att läsa koden. Dessutom tillåter inte standard C++-reglerna för klasser definierade i funktioner att de används i mallar och gör dem därför omöjliga att använda.

Den uppenbara lösningen på problemet var att tillåta definitionen av lambda-uttryck och lambda-funktioner i C++11. Lambdafunktionen definieras så här:

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

Returtypen för denna namnlösa funktion beräknas som decltype(x+y) . Returtypen kan endast utelämnas om lambdafunktionen är av formen . Detta begränsar storleken på lambdafunktionen till ett enda uttryck. return expression

Returtypen kan anges explicit, till exempel:

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

Detta exempel skapar en temporär variabel z för att lagra ett mellanvärde. Som med normala funktioner bevaras inte detta mellanvärde mellan samtalen.

Returtypen kan utelämnas helt om funktionen inte returnerar ett värde (det vill säga returtypen är ogiltig )

Det är också möjligt att använda referenser till variabler definierade i samma omfång som lambdafunktionen. En uppsättning av sådana variabler kallas vanligtvis en stängning . Förslutningar definieras och används enligt följande:

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

Detta kommer att visa summan av alla element i listan. Den totala variabeln lagras som en del av stängningen av lambdafunktionen. Eftersom den refererar till stackvariabeln total kan den ändra dess värde.

Stängningsvariabler för lokala variabler kan också definieras utan att använda referenssymbolen & , vilket betyder att funktionen kommer att kopiera värdet. Detta tvingar användaren att deklarera en avsikt att referera till eller kopiera en lokal variabel.

För lambda-funktioner som garanterat kommer att köras inom sin omfattning är det möjligt att använda alla stackvariabler utan behov av explicita referenser till dem:

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

Implementeringsmetoderna kan variera internt, men lambda-funktionen förväntas lagra en pekare till stacken av funktionen den skapades i, snarare än att arbeta på individuella stackvariabelreferenser.

[&]Om används istället [=]kommer alla använda variabler att kopieras, vilket gör att lambda-funktionen kan användas utanför omfånget för de ursprungliga variablerna.

Standardöverföringsmetoden kan också kompletteras med en lista över individuella variabler. Om du till exempel behöver skicka de flesta variablerna genom referens, och en efter värde, kan du använda följande konstruktion:

int totalt = 0 ; int värde = 5 ; [ & , värde ]( int x ) { totalt += ( x * värde ); } ( 1 ); //(1) anropa lambdafunktion med värde 1

Detta kommer att göra att totalen skickas genom referens och värde efter värde.

Om en lambda-funktion är definierad i en klassmetod anses den vara en vän till den klassen. Sådana lambda-funktioner kan använda en referens till ett objekt av klasstypen och komma åt dess interna fält:

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

Detta fungerar bara om omfattningen av lambda-funktionen är en klassmetod SomeType .

Arbetet med denna pekare till objektet som den aktuella metoden interagerar med implementeras på ett speciellt sätt. Det måste uttryckligen markeras i lambdafunktionen:

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

Genom att använda ett formulär [&]eller [=]en lambdafunktion blir detta tillgängligt automatiskt.

Typen av lambdafunktioner är implementeringsberoende; namnet på denna typ är endast tillgängligt för kompilatorn. Om du behöver skicka en lambdafunktion som parameter måste den vara en malltyp, eller lagras med std::function . Nyckelordet auto låter dig spara en lambdafunktion lokalt:

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

Dessutom, om funktionen inte tar några argument, kan ()du utelämna:

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

Ibland finns det ett behov av att implementera en funktionsmall som skulle resultera i ett uttryck som har samma typ och samma värdekategori som något annat uttryck.

mall < typnamn LHS , typnamn RHS > RETURN_TYPE AddingFunc ( const LHS & lhs , const RHS & rhs ) // vad ska RETURN_TYPE vara? { returnera lhs + rhs ; }

För att uttrycket AddingFunc(x, y) ska ha samma typ och samma värdekategori som uttrycket lhs + rhs när de ges argumenten x och y , kan följande definition användas inom C++11:

mall < typnamn LHS , typnamn RHS > decltype ( std :: declval < const LHS &> () + std :: declval < const RHS &> ()) AddingFunc ( const LHS & lhs , const RHS & rhs ) { returnera lhs + rhs ; }

Denna notation är något besvärlig, och det skulle vara trevligt att kunna använda lhs och rhs istället för std::declval<const LHS &>() respektive std::declval<const RHS &>(). Dock i nästa version

mall < typnamn LHS , typnamn RHS > decltype ( lhs + rhs ) AddingFunc ( const LHS & lhs , const RHS & rhs ) // Ej giltigt i C++11 { returnera lhs + rhs ; }

mer läsbara för människor, lhs- och rhs-identifierarna som används i decltype -operanden kan inte beteckna alternativ som deklareras senare. För att lösa detta problem introducerar C++11 en ny syntax för att deklarera funktioner med en returtyp i slutet:

mall < typnamn LHS , typnamn RHS > auto AddingFunc ( const LHS & lhs , const RHS & rhs ) -> decltype ( lhs + rhs ) { returnera lhs + rhs ; }

Det bör dock noteras att i den mer generiska AddingFunc-implementeringen nedan drar den nya syntaxen inte fördel av korthet:

mall < typnamn LHS , typnamn RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: framåt < LHS > ( lhs ) + std :: framåt < RHS > ( rhs )) { return std :: framåt < LHS > ( lhs ) + std :: framåt < RHS > ( rhs ); } mall < typnamn LHS , typnamn RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // samma effekt som med std::forward ovan { return std :: framåt < LHS > ( lhs ) + std :: framåt < RHS > ( rhs ); } mall < typnamn LHS , typnamn RHS > decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // samma effekt som att sätta typ i slutet AddingFunc ( LHS && lhs , RHS && rhs ) { return std :: framåt < LHS > ( lhs ) + std :: framåt < RHS > ( rhs ); }

Den nya syntaxen kan användas i enklare deklarationer och deklarationer:

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

Användningen av nyckelordet " " autoi detta fall innebär endast en sen indikation av returtypen och är inte relaterad till dess automatiska slutledning.

Förbättra objektkonstruktörer

Standard C++ tillåter inte att en klasskonstruktor anropas från en annan konstruktor av samma klass; varje konstruktor måste initiera alla medlemmar i klassen helt, eller anropa klassens metoder för att göra det. Icke-konstmedlemmar i en klass kan inte initieras på den plats där dessa medlemmar deklareras.

C++11 blir av med dessa problem.

Den nya standarden tillåter att en klasskonstruktör anropas från en annan (den så kallade delegeringen). Detta gör att du kan skriva konstruktörer som använder beteendet hos andra konstruktörer utan att införa dubblettkod.

Exempel:

klass SomeType { int nummer ; offentliga : SomeType ( int new_number ) : number ( new_number ) {} SomeType () : SomeType ( 42 ) {} };

Från exemplet kan du se att konstruktorn SomeTypeutan argument anropar konstruktorn för samma klass med ett heltalsargument för att initiera variabeln number. En liknande effekt skulle kunna uppnås genom att ange ett initialt värde på 42 för denna variabelrätt vid dess deklaration.

klass SomeType { int nummer = 42 ; offentliga : SomeType () {} explicit SomeType ( int new_number ) : number ( new_number ) {} };

Vilken klasskonstruktör som helst kommer att initialiseras numbertill 42 om den inte själv tilldelar den ett annat värde.

Java , C# och D är exempel på språk som också löser dessa problem .

Det bör noteras att om ett objekt i C++03 anses vara helt skapat när dess konstruktör slutför exekveringen, så kommer resten av konstruktörerna i C++11, efter att minst en delegerande konstruktor har exekveras, att arbeta på ett färdigbyggt föremål. Trots detta kommer objekten i den härledda klassen att konstrueras först efter att alla konstruktörer för basklasserna har exekverats.

Explicit ersättning av virtuella funktioner och finalitet

Det är möjligt att signaturen för en virtuell metod har ändrats i basklassen eller felaktigt inställd i den härledda klassen initialt. I sådana fall kommer den givna metoden i den härledda klassen inte att åsidosätta motsvarande metod i basklassen. Så om programmeraren inte ändrar metodsignaturen korrekt i alla härledda klasser, kanske metoden inte anropas korrekt under programkörningen. Till exempel:

struct Base { virtuell void some_func (); }; struct härledd : Base { void sone_func (); };

Här är namnet på en virtuell funktion som deklareras i en härledd klass felstavat, så en sådan funktion kommer inte att åsidosätta Base::some_func, och kommer därför inte att anropas polymorft genom en pekare eller referens till bassubobjektet.

C++11 kommer att lägga till möjligheten att spåra dessa problem vid kompileringstid (snarare än körtid). För bakåtkompatibilitet är den här funktionen valfri. Den nya syntaxen visas nedan:

struktur B { virtuell void some_func (); virtuell void f ( int ); virtuell void g () const ; }; struktur D1 : offentlig B { void sone_func () åsidosätt ; // fel: ogiltigt funktionsnamn void f ( int ) åsidosätta ; // OK: åsidosätter samma funktion i basklassen virtual void f ( long ) override ; // fel: parametertyp inte matchar virtuell void f ( int ) const åsidosättande ; // fel: funktion cv-kvalificering missmatchar virtuell int f ( int ) åsidosättande ; // fel: returtyp missmatchar virtuell void g () const final ; // OK: åsidosätter samma funktion i basklassen virtual void g ( long ); // OK: ny virtuell funktion }; struktur D2 : Dl { virtuell void g () const ; // fel: försök att ersätta den sista funktionen };

Närvaron av en specificator för en virtuell funktion finalinnebär att dess ytterligare ersättning är omöjlig. Dessutom kan en klass som definieras med den slutliga specifikationen inte användas som en basklass:

struct F final { int x , y ; }; struct D : F // fel: arv från slutklasser inte tillåtet { int z ; };

Identifierarna overrideoch finalhar en speciell betydelse endast när de används i vissa situationer. I andra fall kan de användas som normala identifierare (till exempel som namn på en variabel eller funktion).

Nollpekarkonstant

Sedan tillkomsten av C 1972 har konstanten 0 spelat den dubbla rollen som ett heltal och en nollpekare. Ett sätt att hantera denna tvetydighet som är inneboende i C-språket är makrot NULL, som vanligtvis utför ((void*)0)eller substitutionen 0. C++ skiljer sig från C i detta avseende, och tillåter endast användningen 0av en nollpekare som en konstant. Detta leder till dålig interaktion med funktionsöverbelastning:

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

Om makrot NULLdefinieras som 0(vilket är vanligt i C++), kommer raden foo(NULL);att resultera i ett anrop foo(int), inte foo(char *)som en snabb titt på koden kan antyda, vilket nästan säkert inte är vad programmeraren avsåg.

En av nyheterna med C++11 är ett nytt nyckelord för att beskriva en nollpekarkonstant - nullptr. Denna konstant är av typen std::nullptr_t, som implicit kan konverteras till typen av vilken pekare som helst och jämföras med vilken pekare som helst. Implicit konvertering till en integraltyp är inte tillåten, förutom bool. Det ursprungliga förslaget till standarden tillät inte implicit konvertering till booleskt, men standardutformningsgruppen tillät sådana konverteringar för att vara kompatibla med konventionella pekartyper. Den föreslagna formuleringen ändrades efter en enhällig omröstning i juni 2008 [1] .

För bakåtkompatibilitet kan en konstant 0också användas som en nollpekare.

char * pc = nullptr ; // true int * pi = nullptr ; // true bool b = nullptr ; // höger. b=falskt. int i = nullptr ; // fel foo ( nullptr ); // anropar foo(char *), inte foo(int);

Ofta är konstruktioner där visaren garanterat är tom enklare och säkrare än resten - så du kan överbelasta med . nullptr_t

klass Nyttolast ; klass SmartPtr { SmartPtr () = default ; SmartPtr ( nullptr_t ) {} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< explicit SmartPtr ( nyttolast * aData ) : fData ( aData ) {} // kopiera konstruktorer och op= utelämna ~ SmartPtr () { delete fData ; } privat : Nyttolast * fData = nullptr ; } SmartPtr getPayload1 () { return nullptr ; } // SmartPtr(nullptr_t) överbelastning kommer att anropas. Starkt skrivna enums

I standard C++ är enums inte typsäkra. Faktum är att de representeras av heltal, trots att själva typerna av uppräkningar skiljer sig från varandra. Detta gör det möjligt att göra jämförelser mellan två värden från olika enums. Det enda alternativet som C++03 erbjuder för att skydda enum är att inte implicit konvertera heltal eller element i en enum till element i en annan enum. Dessutom är sättet det representeras i minnet (heltalstyp) implementeringsberoende och därför inte portabelt. Slutligen har uppräkningselement ett gemensamt omfång, vilket gör det omöjligt att skapa element med samma namn i olika uppräkningar.

C++11 erbjuder en speciell klassificering av dessa enums, fri från ovanstående nackdelar. För att beskriva sådana uppräkningar används en deklaration enum class(den kan också användas enum structsom en synonym):

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

En sådan uppräkning är typsäker. Element i en klassenum kan inte implicit konverteras till heltal. Som en konsekvens är jämförelse med heltal också omöjlig (uttrycket Enumeration::Val4 == 101resulterar i ett kompileringsfel).

Klassuppräkningstypen är nu implementeringsoberoende. Som standard, som i fallet ovan, är denna typ int, men i andra fall kan typen ställas in manuellt enligt följande:

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

Omfattningen av enum-medlemmar bestäms av omfattningen av enum-namnet. Att använda elementnamn kräver att man specificerar namnet på klassens enum. Så till exempel är värdet Enum2::Val1definierat, men värdet Val1 är inte definierat.

Dessutom erbjuder C++11 möjligheten att explicit scoping och underliggande typer för vanliga enums:

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

I det här exemplet är enum-elementnamnen definierade i enum-utrymmet (Enum3::Val1), men för bakåtkompatibilitet är elementnamnen också tillgängliga i det gemensamma omfånget.

Även i C++11 är det möjligt att fördeklarera enums. I tidigare versioner av C++ var detta inte möjligt eftersom storleken på en enum berodde på dess element. Sådana deklarationer kan endast användas när storleken på uppräkningen är specificerad (explicit eller implicit):

enum Enum1 ; // ogiltig för C++ och C++11; underliggande typ kan inte bestämmas enum Enum2 : unsigned int ; // true för C++11, underliggande typ uttryckligen specificerad enum- klass Enum3 ; // true för C++11, underliggande typ är int enum klass Enum4 : unsigned int ; // sant för C++11. enum Enum2 : unsigned short ; // ogiltig för C++11 eftersom Enum2 tidigare deklarerades med en annan underliggande typ Vinkelparenteser

Standard C++-tolkare definierar alltid teckenkombinationen ">>" som den högra skiftoperatorn. Avsaknaden av ett mellanslag mellan de avslutande vinkelparenteserna i mallparametrarna (om de är kapslade) behandlas som ett syntaxfel.

C++11 förbättrar tolkarens beteende i det här fallet så att flera rätvinkliga parenteser kommer att tolkas som avslutande mallargumentlistor.

Det beskrivna beteendet kan fixas till förmån för det gamla tillvägagångssättet med hjälp av parenteser.

mall < klass T > klass Y { /* ... */ }; Y < X < 1 >> x3 ; // Rätt, samma som "Y<X<1> > x3;". Y < X < 6 >> 1 >> x4 ; // Syntaxfel. Du måste skriva "Y<X<(6>>1)>> x4;".

Som visas ovan är denna förändring inte helt kompatibel med den tidigare standarden.

Explicita konverteringsoperatorer

C++-standarden tillhandahåller nyckelordet explicitsom en modifierare för enparameterskonstruktörer så att sådana konstruktorer inte fungerar som implicita konverteringskonstruktorer. Detta påverkar dock inte de faktiska konverteringsoperatörerna på något sätt. Till exempel kan en smart pekarklass innehålla operator bool()för att efterlikna en normal pekare. En sådan operator kan till exempel kallas så här: if(smart_ptr_variable)(grenen exekveras om pekaren inte är null). Problemet är att en sådan operatör inte skyddar mot andra oväntade konverteringar. Eftersom typen booldeklareras som en aritmetisk typ i C++, är implicit konvertering till vilken heltalstyp som helst eller till och med till en flyttalstyp möjlig, vilket i sin tur kan leda till oväntade matematiska operationer.

I C++11 explicitgäller nyckelordet även konverteringsoperatorer. Liksom konstruktörer skyddar den mot oväntade implicita omvandlingar. Men situationer där språket kontextuellt förväntar sig en boolesk typ (till exempel i villkorliga uttryck, loopar och logiska operatoroperander) anses vara explicita omvandlingar, och den explicita boolkonverteringsoperatorn anropas direkt.

Mall typedef

I standard C++ kan ett nyckelord typedefendast användas som en synonymdefinition för en annan typ, inklusive som en synonym för en mallspecifikation med alla dess parametrar specificerade. Men det går inte att skapa en mallsynonym. Till exempel:

mall < typnamn Först , typnamn Andra , int tredje > klass SomeType ; mall < typnamnAndra > _ typedef SomeType < OtherType , Second , 5 > TypedefName ; // Inte möjligt i C++

Det här kommer inte att kompilera.

C++11 lade till denna funktion med följande syntax:

mall < typnamn Först , typnamn Andra , int tredje > klass SomeType ; mall < typnamnAndra > _ använder TypedefName = SomeType < OtherType , Second , 5 > ;

I C++11 kan direktivet usingäven användas för att alias en datatyp.

typedef void ( * OtherType )( double ); // Gammal stil med OtherType = void ( * )( double ); // Ny syntax Ta bort restriktioner från fackföreningen

I tidigare C++-standarder finns det ett antal restriktioner för användningen av medlemmar av klasstyper inom fackföreningar. I synnerhet kan fackföreningar inte innehålla objekt med en icke-trivial konstruktor. C++11 tar bort några av dessa restriktioner. [2]

Här är ett enkelt exempel på en join som är tillåten i C++11:

//för placering ny #inkludera <ny> structPoint { _ Punkt () {} Punkt ( int x , int y ) : x_ ( x ), y_ ( y ) {} int x_ , y_ ; }; fackförening U { int z ; dubbel w ; Punkt p ; // Inte sant för C++03 eftersom Point har en icke-trivial konstruktor. Koden fungerar dock korrekt i C++11. U () { new ( & p ) Punkt (); } // Inga icke-triviala metoder är definierade för föreningen. // Om det behövs kan de tas bort för att få den manuella definitionen att fungera };

Ändringarna påverkar inte befintlig kod, eftersom de bara luckrar upp befintliga begränsningar.

Utöka funktionaliteten

Det här avsnittet beskriver nya funktioner som tidigare inte var tillgängliga eller som krävde särskilda icke-portabla bibliotek.

Variable Argument Templates

Före C++11 kunde mallar (av klasser eller funktioner) bara ta ett visst antal argument, definierade när mallen ursprungligen deklarerades. C++11 låter dig definiera mallar med ett variabelt antal argument av vilken typ som helst.

mall < typnamn ... Värden > klass tupel ;

Till exempel accepterar mallklassen tuple ( tuple ) valfritt antal typnamn som mallparametrar:

class tuple < int , std :: vektor < int > , std :: map < std :: string , std :: vektor < int >>> some_instance_name ;

Argument kan saknas, så alternativet class tuple<> some_instance_namekommer också att fungera.

För att förhindra mallinstansiering utan argument kan följande definition användas:

mall < typnamn Först typnamn ... Rest > klass tupel ; _

Variabel-argumentmallar är också tillämpliga på funktioner, vilket gör att de kan användas i typsäkra varianter av variadiska funktioner (som printf) och för att hantera icke-triviala objekt.

mall < typnamn ... Params > void printf ( const std :: string & str_format , Params ... parametrar );

Operatören ... spelar här två roller. Till vänster om Params meddelar en operatör behovet av att packa parametrar. Genom att använda packade parametrar kan 0 eller fler argument associeras med en mall. Packade parametrar kan användas för mer än att bara skicka typnamn. Operatorn ... till höger packar i sin tur upp parametrarna i separata argument (se args...funktionskroppen i exemplet nedan).

Det är också möjligt att rekursivt använda mallar med ett varierande antal argument. Ett exempel skulle vara den typsäkra ersättningen för printf :

void printf ( const char * s ) { while ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) throw std :: runtime_error ( "ogiltig formatsträng: saknade argument" ); std :: cout << * s ++ ; } } mall < typnamn T , typnamn ... Args > void printf ( const char * s , T - värde , Args ... args ) { while ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) { std :: cout << värde ; ++ s ; printf ( s , args ...); // fortsätt bearbeta argument även om *s == 0 returnerar ; } std :: cout << * s ++ ; } throw std :: logic_error ( "extra argument tillhandahållna till printf" ); }

Detta mönster är rekursivt. Observera att printf-funktionen anropar resultaten av att instansiera sig själv eller basfunktionen printf om args... är tom.

Det finns inget enkelt sätt att kringgå parametrar i en variadisk mall. Trots detta kan man kringgå detta problem genom att använda argumentet uppackningsoperatör.

Till exempel kan en klass definieras så här:

mall < typnamn ... BaseClasses > class ClassName : public BaseClasses ... { offentliga : ClassName ( BaseClasses && ... base_classes ) : BaseClasses ( base_classes )... {} };

Uppackningsoperatören kommer att duplicera alla typer av överordnade klasser ClassNamepå ett sådant sätt att klassen kommer att ärvas från alla typer som anges i mallparametrarna. Dessutom måste konstruktören acceptera en referens till alla basklasser så att varje överordnad basklass initieras ClassName.

Mallparametrar kan omdirigeras. I kombination med rvalue-referenser (se ovan) kan du omdirigera:

mall < typnamn TypeToConstruct > struct SharedPtrAllocator { mall < typnamn ... Args > std :: shared_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( ny TypeToConstruct ( std :: framåt < Args > ( params )...)); }; };

Denna kod packar upp argumentlistan i TypeToConstruct-konstruktorn. Syntaxen std::forward<Args>(params)tillåter dig att helt transparent omdirigera argument till konstruktorn, oavsett deras rvalue-karaktär. Funktionen lindar automatiskt in pekare std::shared_ptrför att ge skydd mot minnesläckor.

Det är också möjligt att ange antalet packade argument enligt följande:

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

Här SomeStruct<Type1, Type2>::sizeär det lika med 2 och SomeStruct<>::sizelika med 0.

Nya strängliteraler

C++03 erbjöd två typer av strängliteraler. Den första typen, en sträng med dubbla citattecken, är en nollterminerad array av typen const char. Den andra typen, definierad som L"", är en nollterminerad array av typen const wchar_t, där det wchar_tfinns en bred karaktär av obestämda storlekar och semantik. Ingen av de bokstavliga typerna är avsedda att stödja UTF-8 , UTF-16 strängliterals eller någon annan typ av Unicode - kodning

Typdefinitionen charhar modifierats för att uttryckligen säga att den är åtminstone den storlek som behövs för att lagra en åtta-bitars UTF-8- kodning och tillräckligt stor för att innehålla vilket tecken som helst i runtime-teckenuppsättningen. Tidigare i standarden definierades denna typ som ett enda tecken, senare, efter C-språkstandarden, blev den garanterad att uppta minst 8 bitar.

Det finns tre Unicode-kodningar som stöds i C++11-standarden: UTF-8 , UTF-16 och UTF-32 . Förutom ovanstående ändringar av den inbyggda charteckentypen lägger C++11 till två nya teckentyper: char16_toch char32_t. De är designade för att lagra UTF-16- respektive UTF-32-tecken.

Följande visar hur man skapar strängliteraler för var och en av dessa kodningar:

u8 "Jag är en UTF-8-sträng." u "Detta är en UTF-16-sträng." U "Detta är en UTF-32-sträng."

Typen av den första raden är normal const char[]. Typen av den andra raden är const char16_t[]. Typen av den tredje raden är const char32_t[].

När man konstruerar strängliteraler i Unicode-standarden är det ofta användbart att infoga Unicode-koden direkt i strängen. C++11 tillhandahåller följande syntax för detta:

u8 "Detta är ett Unicode-tecken: \u2018 ." u "Detta är ett större Unicode-tecken: \u2018 ." U "Detta är ett Unicode-tecken: \U00002018 ."

Siffran efter \umåste vara hexadecimalt; du behöver inte använda prefixet 0x. Identifieraren \ubetyder en 16-bitars Unicode-kod; för att ange en 32-bitars kod används \Uockså ett 32-bitars hexadecimalt tal. Endast giltiga Unicode-koder kan anges. Till exempel är koder i intervallet U+D800-U+DFFF inte tillåtna eftersom de är reserverade för UTF-16 surrogatpar.

Det är också ibland användbart att undvika manuellt escape-strängar, särskilt när du använder XML -fillitterals, skriptspråk eller reguljära uttryck. För dessa ändamål stöder C++11 "rå" strängliteraler:

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

I det första fallet är allt mellan "(och )"en del av strängen. Karaktärerna "och \behöver inte fly. I det andra fallet "delimiter(startar den en sträng, och den slutar först när den når )delimiter". Strängen delimiterkan vara vilken sträng som helst på upp till 16 tecken, inklusive den tomma strängen. Den här strängen kan inte innehålla mellanslag, kontrolltecken, ' (', ' )' eller tecknet ' \'. Genom att använda den här avgränsningssträngen kan tecknet ' )' användas i obearbetade strängar. Till exempel R"delimiter((a-z))delimiter"motsvarar det "(a-z)"[3] .

"Rå" strängliteral kan kombineras med en utökad uppsättning literal (prefix L"") eller valfri Unicode literal prefix.

LR"(Rå bred sträng bokstavlig \t (utan tabb))" u8R"XXX(Jag är en "rå UTF-8"-sträng.)XXX" uR"*(Detta är en "rå UTF-16"-sträng.)*" UR"(Detta är en "rå UTF-32"-sträng.)" Anpassade bokstaver

Anpassade bokstaver implementeras med operatörsöverbelastning operator"". Bokstaver kan vara inline- eller constexpr-kvalificerare . Det är önskvärt att det bokstavliga börjar med ett understreck, eftersom det kan finnas en konflikt med framtida standarder. Till exempel hör det bokstavliga i redan till de komplexa talen från std::complex.

Bokstaver kan bara ta en av följande typer: const char * , unsigned long long int , long double , char , wchar_t , char16_t , char32_t. Det räcker med att överbelasta bokstaven endast för typen const char * . Om ingen mer lämplig kandidat hittas kommer en operatör med den typen att anropas. Ett exempel på att konvertera miles till kilometer:

constexpr int operator "" _mi ( osignerad lång lång int i ) { return 1,6 * i ;}

Strängliteraler tar ett andra argument std::size_toch ett av de första: const char * , const wchar_t *, const char16_t * , const char32_t *. Strängbokstavar gäller för poster omgivna av dubbla citattecken.

Flertrådad minnesmodell

C++11 standardiserar stöd för flertrådsprogrammering. Det finns två delar inblandade: en minnesmodell som tillåter flera trådar att samexistera i ett program, och ett bibliotek som stöder kommunikation mellan trådar.

Minnesmodellen definierar hur flera trådar kan komma åt samma minnesplats och definierar när ändringar gjorda av en tråd blir synliga för andra trådar.

Trådad lagring Explicit standard och borttagning av speciella metoder

Specifierar defaultoch deletekan anges istället för metodkroppen.

klass Foo { offentliga : foo () = default ; Foo ( int x ) { /* ... */ } };

Specifieraren defaultbetyder standardimplementeringen och kan endast tillämpas på speciella medlemsfunktioner:

  • standardkonstruktör;
  • kopia konstruktör;
  • flytta konstruktör;
  • uppdragsoperatör;
  • flytta operatör;
  • förstörare.

Specifieraren deletemarkerar de metoder som inte går att arbeta med. Tidigare var du tvungen att deklarera sådana konstruktörer i klassens privata omfattning.

klass Foo { offentliga : foo () = default ; Foo ( const Foo & ) = ta bort ; void bar ( int ) = radera ; void bar ( dubbel ) {} }; // ... Foo obj ; obj . stång ( 5 ); // fel! obj . bar ( 5,42 ); // okej Skriv long long int

Heltalstypen long long intanges i C99 och används de facto flitigt i C++. Nu ingår den officiellt i standarden.

Statisk diagnostik

C++11 har två statiska diagnostiska mekanismer:

  • Nyckelordet static_assertger ett kompileringsfel om uttrycket i parentes är falskt.
  • Ett bibliotek type_traitssom innehåller mallar som tillhandahåller typinformation vid kompilering.
#include <type_traits> mall < classT > _ void körning ( T * aData , size_t n ) { static_assert ( std :: is_pod < T >:: value , "Typen T måste vara enkel." ); ... } Arbeta med storleken på datamedlemmar i klasser utan att skapa ett objekt

C++03 gjorde det möjligt för operatorn sizeofatt användas på enkla typer och objekt. Men följande konstruktion var ogiltig:

struct SomeType { OtherType member ; }; sizeof ( SomeType :: medlem ); //Fungerar inte i C++03, men sant i C++11.

Resultatet av detta samtal bör vara en storlek OtherType. C++03 stöder inte ett sådant anrop och den här koden kommer inte att kompileras. C++11 tillåter sådana konstruktioner.

Objektjusteringskontroll och justeringsbegäranden

C++11 låter dig justera variabler med hjälp av operatorerna alignofoch alignas.

alignoftar en typ och returnerar antalet byte som objektet kan skiftas med. Till exempel struct X { int n; char c; };, för 8 byte, alignofreturnerar den värdet 4. För länkar returnerar den värdet för länktypen; för arrayer, värdet för arrayelementet

alignasstyr justeringen av ett objekt i minnet. Du kan till exempel ange att en char-array måste vara korrekt justerad för att lagra typen float:

alignas ( float ) unsigned char c [ sizeof ( float )] Tillåter implementeringar med en sopsamlare Attribut

Ändringar av C++ standardbiblioteket

Ändringar av befintliga komponenter

  • När den klistras in std::setvet programmeraren ibland vilken position det nya elementet kommer att hamna i. För detta används en valfri parameter - "tips"; om gissningen är korrekt kommer tidsuppskattningen att vara en amorterad konstant, inte O(log n) . Betydelsen av "tips" i C++11 ändrades: tidigare betydde det elementet före det nuvarande, vilket inte är helt korrekt: det är inte klart vad man ska göra om infogningen är i första positionen. Nu är detta elementet efter det nuvarande.
  • En bekväm mall har skrivits som anropar konstruktörer utan minnesallokering - std::allocator_traits<>::construct(). En metod har lagts till i alla behållare emplacesom skapar ett objekt på plats.
  • Lade till nya språkfunktioner för C++11.
  • Lade till metoder cbeginoch cendskapade garanterat konst iteratorer. Bekvämt för metaprogrammering, för inställningstyper via auto.
  • I behållare som startar minnet med en marginal har en funktion dykt upp shrink_to_fit.
  • B std::listsätter strängare gränser för vad som görs i O ( n ), och vad som görs i konstant tid.
  • Lade till std::vectordirekt minnesåtkomst via data().
  • Förbjud flera std::stringatt referera till samma minne. Tack vare detta dök direkt åtkomst genom front(), vilket är bekvämt, till exempel för interaktionen av sträng och WinAPI , upp .

Flödeskontroll

Medan språket C++03 tillhandahåller en minnesmodell som stöder multithreading, tillhandahålls det huvudsakliga stödet för att faktiskt använda multithreading av standardbiblioteket C++11.

En trådklass ( std::thread) tillhandahålls som accepterar ett funktionsobjekt (och en valfri lista med argument att skicka till det) för att köras på en ny tråd. Du kan tvinga en tråd att stoppa innan en annan körande tråd har slutförts genom att tillhandahålla stöd för trådpoolning genom en medlemsfunktion std::thread::join(). Om möjligt tillhandahålls åtkomst till trådens inbyggda handtag för plattformsspecifika operationer via medlemsfunktionen std::thread::native_handle().

För synkronisering mellan trådar läggs lämpliga mutexer ( std::mutex, std::recursive_mutexetc.) och villkorsvariabler ( std::condition_variableoch std::condition_variable_any) till i biblioteket. De är tillgängliga genom resursinitiering (RAII) lås ( std::lock_guardoch std::unique_lock) och låsalgoritmer för enkel användning.

Högpresterande arbete på låg nivå kräver ibland kommunikation mellan trådar utan överbelastning av mutexes. Detta görs med hjälp av atomoperationer på minnesplatser. De kan valfritt ange de lägsta minnessynlighetsgränserna som krävs för operationen. Explicita minnesbarriärer kan också användas för detta ändamål.

C++11-trådbiblioteket innehåller också terminer och löften för att skicka asynkrona resultat mellan trådar, och en klass std::packaged_taskför att slå ett funktionsanrop som kan generera ett sådant asynkront resultat. Terminsförslaget har kritiserats då det saknar ett sätt att kombinera terminer och kontrollera uppfyllandet av ett enda löfte i en uppsättning löften.

Ytterligare trädningsmöjligheter på hög nivå, såsom trådpooler, har placerats i en framtida C++ white paper. De är inte en del av C++11, men deras slutliga implementering förväntas byggas helt ovanpå trådningsbibliotekets funktioner.

Den nya funktionen std::asyncger ett bekvämt sätt att köra uppgifter och binda resultatet av deras exekvering till ett objekt i std::future. Användaren kan välja om jobbet ska köras asynkront på en separat tråd, eller synkront på den aktuella tråden i väntan på värdet.

Hashtabeller

std::hash_setoch std::hash_maphar länge varit en icke-standardiserad STL-tillägg, faktiskt implementerad i de flesta kompilatorer. I C++11 blev de standard, under namnen std::unordered_setoch std::unordered_map. Även om de i själva verket är hashtabeller och standarden inte lämnar mycket rörelseutrymme, ges namnen i C++-stil: inte "hur de är implementerade", utan "vad de är".

Reguljära uttryck

Det nya biblioteket, deklarerat i rubrikfilen <regex>, innehåller flera nya klasser:

  • Reguljära uttryck representeras som instanser av std::regex;
  • sökresultat representeras som mallinstanser std::match_results.

Funktionen std::regex_searchanvänds för sökning, för 'hitta och ersätt' operationen används funktionen std::regex_replace. Funktionen returnerar en sträng efter att ha utfört ersättningen. std::regex_searchAlgoritmerna och tar std::regex_replaceett reguljärt uttryck och en sträng som indata och returnerar de hittade resultaten som en instans av std::match_results.

Användningsexempel std::match_results:

const char * reg_esp = "[ ,. \\ t \\ n;:]" ; // Lista över separator tecken. // samma sak kan göras med "rå"-strängar: // const char *reg_esp = R"([ ,.\t\n;:])"; std :: regex rgx ( reg_esp ); // 'regex' är en instans av mallklassen // 'basic_regex' med mallparametern 'char'. std :: cmatch match ; // 'cmatch' är en instans av mallklassen // 'match_results' med mallparametern 'const char *'. const char * target = "Osett universitet - Ankh-Morpork" ; // Fixar alla ord i strängen 'target' separerade med tecken från 'reg_esp'. if ( std :: regex_search ( target , match , rgx ) ) { // Om orden separerade av de givna tecknen finns i strängen. const size_t n = matchning . storlek (); för ( storlek_t a = 0 ; a < n ; a ++ ) { std :: string str ( matcha [ a ]. först , matcha [ a ]. andra ); std :: cout << str << " \n " ; } }

Observera att dubbla snedstreck krävs eftersom C++ använder snedstreck för att undvika tecken. Du kan använda "råsträngar" - en annan innovation av C++11-standarden.

Biblioteket <regex>kräver ingen modifiering av befintliga rubrikfiler, inte heller installation av ytterligare språktillägg.


Utökningsbara klasser för generering av slumptal

C-standardbiblioteket tillät generering av pseudoslumptal med hjälp av rand. Dess beteende kan dock variera beroende på implementeringen.

Denna funktion är uppdelad i två delar: generatormotorn, som innehåller det aktuella tillståndet för slumptalsgeneratorn och producerar pseudoslumptal, och fördelningen, som bestämmer räckvidden och den matematiska fördelningen av resultatet. Kombinationen av dessa två objekt skapar en slumptalsgenerator.

Generatormotorer:

Distributioner:

Exempel:

#inkludera <random> #inkludera <funktionell> std :: uniform_int_distribution < int > distribution ( 0 , 99 ); std :: mt19937motor ; _ // Mersenne vortex MT19937 auto generator = std :: bind ( distribution , motor ); int random = generator (); // Få ett slumptal mellan 0 och 99. int random2 = distribution ( motor ); // Få ett slumptal med hjälp av motorn och distribution direkt.



Planerade funktioner som inte ingår i standarden

Moduler Den enorma volymen header-filer ledde till en kvadratisk ökning av kompileringstiden: både mängden kod och antalet moduler i en enda kompileringsenhet ökar. Moduler bör tillhandahålla en mekanism som liknar Delphi DCU-filer eller Java -klassfiler .

Borttagna eller utfasade funktioner

Se även

Anteckningar

  1. Herb Sutter , Vi har en internationell standard: C++0x är enhälligt godkänd Arkiverad 11 december 2018 på Wayback Machine
  2. Scott Meyers , Sammanfattning av C++11-funktionstillgänglighet i gcc och MSVC Arkiverad 26 oktober 2011 på Wayback Machine , 16 augusti 2011
  3. ISO , ISO/IEC 14882:2011 Arkiverad 29 januari 2013 på Wayback Machine
  4. C++0x-namn definierat i det slutliga utkastet N3290 Arkiverad 20 juni 2010 på Wayback Machine
  5. Stroustrup, Bjorn  - C++0x - nästa ISO C++-standard Arkiverad 11 maj 2011 på Wayback Machine
  6. C++ Standards Committee Papers . Hämtad 24 februari 2008. Arkiverad från originalet 18 mars 2010.
  7. C++-källan Bjarne Stroustrup ( 2 januari 2006 ) En kort titt på C++0x . (Engelsk)

Dokument från C++ Standards Committee

  •   Dokument nr. 1401: Jan Kristoffersen (21 oktober 2002)Atomoperationer med flertrådiga miljöer
  •   Dokument nr. 1402: Doug Gregor (22 oktober 2002)Ett förslag om att lägga till en polymorf funktionsobjektomslag till standardbiblioteket
  •   Dokument nr. 1403: Doug Gregor (8 november 2002)Förslag om att lägga till tuppeltyper i standardbiblioteket
  •   Dokument nr. 1424: John Maddock (3 mars 2003)Ett förslag om att lägga till typegenskaper till standardbiblioteket
  •   Dokument nr. 1429: John Maddock (3 mars 2003)Ett förslag om att lägga till reguljära uttryck till standardbiblioteket
  •   Dokument nr. 1449: B. Stroustrup, G. Dos Reis, Mat Marcus, Walter E. Brown, Herb Sutter (7 april 2003)Förslag om att lägga till mallalias till C++
  •   Dokument nr. 1450: P. Dimov, B. Dawes, G. Colvin (27 mars 2003)Ett förslag om att lägga till smarta pekare för allmänna syften till bibliotekets tekniska rapport (revision 1)
  •   Dokument nr. 1452: Jens Maurer (10 april 2003)Ett förslag om att lägga till en utökningsbar slumpnummeranläggning till standardbiblioteket (revision 2)
  •   Dokument nr. 1453: D. Gregor, P. Dimov (9 april 2003)Ett förslag om att lägga till ett referensomslag till standardbiblioteket (revision 1)
  •   Dokument nr. 1454: Douglas Gregor, P. Dimov (9 april 2003)En enhetlig metod för att beräkna funktionsobjektreturtyper (revision 1)
  •   Dokument nr. 1456: Matthew Austern (9 april 2003)Ett förslag om att lägga till hashtabeller till standardbiblioteket (revision 4)
  •   Dokument nr. 1471: Daveed Vandevoorde (18 april 2003)Reflekterande metaprogrammering i C++
  •   Dokument nr. 1676: Bronek Kozicki (9 september 2004)Överbelastad kopiauppdragsoperatör för icke-medlem
  •   Dokument nr. 1704: Douglas Gregor, Jaakko Järvi, Gary Powell (10 september 2004)Variadic Templates: Exploring the Design Space
  •   Dokument nr. 1705: J. Järvi, B. Stroustrup, D. Gregor, J. Siek, G. Dos Reis (12 september 2004)Decltype (och auto)
  •   Dokument nr. 1717: Francis Glassborow, Lois Goldthwaite (5 november 2004)explicita klass- och standarddefinitioner
  •   Dokument nr. 1719: Herb Sutter, David E. Miller (21 oktober 2004)Starkt skrivna uppräkningar (revision 1)
  •   Dokument nr. 1720: R. Klarer, J. Maddock, B. Dawes, H. Hinnant (20 oktober 2004)Förslag om att lägga till statiska påståenden till kärnspråket (revision 3)
  •   Dokument nr. 1757: Daveed Vandevoorde (14 januari 2005)Right Angle Brackets (Revision 2)
  •   Dokument nr. 1811: J. Stephen Adamczyk (29 april 2005)Lägger till den långa långa typen till C++ (revision 3)
  •   Dokument nr. 1815: Lawrence Crowl (2 maj 2005)ISO C++ strategisk plan för multitrådning
  •   Dokument nr. 1827: Chris Uzdavinis, Alisdair Meredith (29 augusti 2005)An Explicit Override Syntax for C++
  •   Dokument nr. 1834: Detlef Vollmann (24 juni 2005)A Pleading for Reasonable Parallel Processing Support in C++
  •   Dokument nr. 1836: ISO/IEC DTR 19768 (24 juni 2005)Utkast till teknisk rapport om C++ bibliotekstillägg
  •   Dokument nr. 1886: Gabriel Dos Reis, Bjarne Stroustrup (20 oktober 2005)Specificerar C++-koncept
  •   Dokument nr. 1891: Walter E. Brown (18 oktober 2005)Framsteg mot Opaque Typedefs för C++0X
  •   Dokument nr. 1898: Michel Michaud, Michael Wong (6 oktober 2004)Speditörer och ärvda konstruktörer
  •   Dokument nr. 1919: Bjarne Stroustrup, Gabriel Dos Reis (11 december 2005)Initialiseringslistor
  •   Dokument nr. 1968: V Samko J Willcock, J Järvi, D Gregor, A Lumsdaine (26 februari 2006)Lambda-uttryck och stängningar för C++
  •   Dokument nr. 1986: Herb Sutter, Francis Glassborow (6 april 2006)Delegerande konstruktörer (revision 3)
  •   Dokument nr. 2016: Hans Boehm, Nick Maclaren (21 april 2002)Should volatile Acquire Atomicity and Thread Visibility Semantics?
  •   Dokument nr. 2142: ISO/IEC DTR 19768 (12 januari 2007)Status för C++ Evolution (mellan Portland och Oxford 2007 möten)
  •   Dokument nr. 2228: ISO/IEC DTR 19768 (3 maj 2007)State of C++ Evolution (Oxford 2007 Meetings)
  •   Dokument nr. 2258: G. Dos Reis och B. StroustrupTemplates Alias
  •   Dokument nr. 2280: Lawrence Crowl (2 maj 2007)Thread-Local Storage
  •   Dokument nr. 2291: ISO/IEC DTR 19768 (25 juni 2007)Status för C++ Evolution (Toronto 2007 Meetings)
  •   Dokument nr. 2336: ISO/IEC DTR 19768 (29 juli 2007)Status för C++ Evolution (Toronto 2007 Meetings)
  •   Dokument nr. 2389: ISO/IEC DTR 19768 (7 augusti 2007)Status för C++ Evolution (före Kona 2007 Meetings)
  •   Dokument nr. 2431: SC22/WG21/N2431 = J16/07-0301 (2 oktober 2007),Ett namn för nollpekaren: nullptr
  •   Dokument nr. 2432: ISO/IEC DTR 19768 (23 oktober 2007)Status för C++ Evolution (post-Kona 2007 Meeting)
  •   Dokument nr. 2437: Lois Goldthwaite (5 oktober 2007)Explicita konverteringsoperatörer
  •   Dokument nr. 2461: ISO/IEC DTR 19768 (22 oktober 2007)Working Draft, standard för programmeringsspråk C++
  •   Dokument nr. 2507: ISO/IEC DTR 19768 (4 februari 2008)Status för C++ Evolution (före Bellevue 2008 möte)
  •   Dokument nr. 2544: Alan Talbot, Lois Goldthwaite, Lawrence Crowl, Jens Maurer (29 februari 2008)Obegränsade fackföreningar
  •   Dokument nr. 2565: ISO/IEC DTR 19768 (7 mars 2008)Status för C++ Evolution (post-Bellevue 2008 Meeting)
  •   Dokument nr. 2597: ISO/IEC DTR 19768 (29 april 2008)Status för C++ Evolution (för mötet mot Antipolis 2008)
  •   Dokument nr. 2606: ISO/IEC DTR 19768 (19 maj 2008)Working Draft, standard för programmeringsspråk C++
  •   Dokument nr. 2697: ISO/IEC DTR 19768 (15 juni 2008)Protokoll från WG21-möte 8-15 juni 2008
  •   Dokument nr. 2798: ISO/IEC DTR 19768 (4 oktober 2008)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 2857: ISO/IEC DTR 19768 (23 mars 2009)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 2869: ISO/IEC DTR 19768 (28 april 2009)State of C++ Evolution (post-San Francisco 2008 Meeting)
  •   Dokument nr. 3000: ISO/ISC DTR 19769 (9 november 2009)Working Draft, standard för programmeringsspråk C++
  •   Dokument nr. 3014: Stephen D. Clamage (4 november 2009)AGENDA, PL22.16 Möte nr. 53, WG21 Möte nr. 48, 8-13 mars 2010, Pittsburgh, PA
  •   Dokument nr. 3082: Herb Sutter (13 mars 2010)C++0x mötesschema
  •   Dokument nr. 3092: ISO/ISC DTR 19769 (26 mars 2010)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 3126: ISO/ISC DTR 19769 (21 augusti 2010)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 3225: ISO/ISC DTR 19769 (27 november 2010)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 3242: ISO/ISC DTR 19769 (28 februari 2011)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 3291: ISO/ISC DTR 19769 (5 april 2011)Arbetsutkast, standard för programmeringsspråk C++
  •   Dokument nr. 3290: ISO/ISC DTR 19769 (5 april 2011)FDIS, standard för programmeringsspråk C++
  •   Dokument nr. 3337 : Datum: 2012-01-16 Arbetsutkast, standard för programmeringsspråk C++

Länkar

Litteratur

  • Stanley B. Lippman, Josy Lajoye, Barbara E. Moo. C++ programmeringsspråk. Core Course 5th Edition = C++ Primer (5th Edition). - M. : "Williams" , 2014. - 1120 sid. - ISBN 978-5-8459-1839-0 .
  • Siddhartha Rao. Teach Yourself C++ in 21 Days, 7th Edition = Sams Teach Yourself C++ in One Hour a Day, 7th Edition. - M. : "Williams" , 2013. - 688 sid. — ISBN 978-5-8459-1825-3 .
  • Stephen Prata. C++ programmeringsspråk (C++11). Föreläsningar och övningar, 6:e upplagan = C++ Primer Plus, 6:e upplagan (utvecklarens bibliotek). - M. : "Williams" , 2012. - 1248 sid. - ISBN 978-5-8459-1778-2 .