C++11

Aktuální verze stránky ještě nebyla zkontrolována zkušenými přispěvateli a může se výrazně lišit od verze recenzované 15. září 2020; kontroly vyžadují 24 úprav .

C++11 [1] [2] nebo ISO/IEC 14882:2011 [3] (v procesu práce na standardu měl kódové označení C++0x [4] [5] ) — nová verze jazykový standard C++ namísto dříve platné ISO /IEC 14882:2003. Nový standard zahrnuje doplňky k jádru jazyka a rozšíření standardní knihovny, včetně většiny TR1  – snad kromě knihovny speciálních matematických funkcí. Nové verze norem spolu s některými dalšími normalizačními dokumenty C++ jsou zveřejněny na webu komise ISO C++ [6] . Příklady programování v C++

Programovací jazyky procházejí postupným rozvojem svých schopností (v současné době jsou po C++11 publikována standardní rozšíření: C++14, C++17, C++20). Tento proces nevyhnutelně způsobuje problémy s kompatibilitou se stávajícím kódem. Příloha C.2 [diff.cpp03] Final Draft International Standard N3290 popisuje některé nekompatibility mezi  C++11 a C++03.

Navrhované změny standardu

Jak již bylo zmíněno, změny se dotknou jak jádra C++, tak jeho standardní knihovny.

Při vývoji každé části budoucího standardu výbor použil řadu pravidel:

Pozornost je věnována začátečníkům, kteří budou vždy tvořit většinu programátorů. Mnoho začátečníků se nesnaží prohloubit své znalosti C++, omezují se na jeho použití při práci na úzkých specifických úkolech [7] . Navíc, vzhledem k všestrannosti C++ a šíři jeho použití (včetně různých aplikací a programovacích stylů), se i profesionálové mohou setkat s novými paradigmaty programování .

Rozšíření základního jazyka

Primárním úkolem komise je vyvinout jádro jazyka C++. Jádro bylo výrazně vylepšeno, byla přidána podpora multithreadingu , vylepšena podpora generického programování , sjednocena inicializace a práce na zlepšení jeho výkonu.

Pro pohodlí jsou vlastnosti a změny jádra rozděleny do tří hlavních částí: vylepšení výkonu, vylepšení pohodlí a nové funkce. Jednotlivé prvky mohou patřit do několika skupin, ale budou popsány pouze v jedné - nejvhodnější.

Zlepšení výkonu

Tyto jazykové komponenty jsou zavedeny za účelem snížení režie paměti nebo zlepšení výkonu.

Dočasné odkazy na objekty a přesunutí sémantiky

Podle standardu C++ lze dočasný objekt vyplývající z vyhodnocení výrazu předat funkcím, ale pouze konstantním odkazem ( const & ). Funkce není schopna určit, zda lze předaný objekt považovat za dočasný a modifikovatelný (konstační objekt, který lze také předat takovým odkazem, nelze upravit (legálně)). To není problém pro jednoduché struktury, jako complexje , ale pro složité typy, které vyžadují alokaci-dealokaci paměti, může být zničení dočasného objektu a vytvoření trvalého časově náročné, zatímco ukazatele lze jednoduše předat přímo.

C++11 zavádí nový typ odkazu , odkaz rvalue .  Jeho deklarace je: type && . Nová pravidla pro rozlišení přetížení vám umožňují používat různé přetížené funkce pro nekonstantní dočasné objekty, označené rvalues, a pro všechny ostatní objekty. Tato inovace umožňuje implementaci tzv. sémantiky pohybu .

Jedná se například std::vector o jednoduchý obal kolem C-pole a proměnné, která ukládá jeho velikost. Kopírovací konstruktor std::vector::vector(const vector &x)vytvoří nové pole a zkopíruje informace; konstruktor přenosu std::vector::vector(vector &&x)může jednoduše vyměňovat ukazatele a proměnné obsahující délku.

Příklad reklamy.

šablona < class T > class vector { vektor ( const vector & ); // Kopírování konstruktoru (pomalý) vektor ( vector && ); // Přenos konstruktoru z dočasného objektu (rychlý) vector & operator = ( const vector & ); // Běžné přiřazení (pomalé) vector & operator = ( vector && ); // Přesunutí dočasného objektu (rychle) void foo () & ; // Funkce, která funguje pouze na pojmenovaném objektu (pomalá) void foo () && ; // Funkce, která funguje pouze pro dočasný objekt (rychle) };

S dočasnými odkazy je spojeno několik vzorů, z nichž dva nejdůležitější jsou a . První z nich dělá z běžného pojmenovaného objektu dočasnou referenci: moveforward

// std::move template example void bar ( std :: string && x ) { static std :: stringsomeString ; _ someString = std :: move ( x ); // uvnitř funkce x=string&, tedy druhý tah pro volání přiřazení pohybu } std :: stringy ; _ bar ( std :: pohyb ( y )); // první tah změní řetězec& na řetězec&& do lišty volání

Šablona se používá pouze v metaprogramování, vyžaduje explicitní parametr šablony (má dvě nerozlišitelná přetížení) a je spojena se dvěma novými mechanismy C++. První z nich je lepení odkazů: , pak . Za druhé, výše uvedená funkce bar() vyžaduje na vnější straně dočasný objekt, ale uvnitř je parametr x obyčejný pojmenovaný (lvalue) pro nouzový přechod, což znemožňuje automatické rozlišení parametru string& od parametru string&&. V běžné funkci bez šablony programátor může nebo nemusí vložit move(), ale co šablona? forwardusing One=int&&; using Two=One&;Two=int&

// příklad použití šablony std::forward class Obj { std :: stringfield ; _ šablona < classT > _ Obj ( T && x ) : pole ( std :: vpřed < T > ( x )) {} };

Tento konstruktor pokrývá přetížení regulárního (T=řetězec&), kopírování (T=konst řetězce&) a přesunu (T=řetězec) pomocí lepení odkazů. A forward nedělá nic nebo se expanduje na std::move v závislosti na typu T a konstruktor zkopíruje, pokud se jedná o kopii, a přesune, pokud se jedná o přesun.

Obecné konstantní výrazy

C++ měl vždy koncept konstantních výrazů. Výrazy jako 3+4 tedy vždy vrátily stejné výsledky, aniž by způsobily nějaké vedlejší účinky. Konstantní výrazy samy o sobě poskytují kompilátorům C++ pohodlný způsob, jak optimalizovat výsledek kompilace. Překladače vyhodnocují výsledky takových výrazů pouze v době kompilace a ukládají již vypočítané výsledky do programu. Takové výrazy se tedy vyhodnocují pouze jednou. Existuje také několik případů, kdy jazyková norma vyžaduje použití konstantních výrazů. Takovými případy mohou být například definice externích polí nebo hodnot enum.


int GiveFive () { return 5 ;} int nějaká_hodnota [ GiveFive () + 7 ]; // vytvoří pole 12 celých čísel; zakázáno v C++

Výše uvedený kód je v C++ nezákonný, protože GiveFive() + 7 není technicky konstantní výraz známý v době kompilace. Kompilátor v tu chvíli prostě neví, že funkce skutečně vrací konstantu za běhu. Důvodem této úvahy kompilátoru je to, že tato funkce může ovlivnit stav globální proměnné, volat jinou nekonstantní běhovou funkci a tak dále.

C++11 zavádí klíčové slovo constexpr , které uživateli umožňuje zajistit, aby buď funkce nebo konstruktor objektu vrátil konstantu v době kompilace. Výše uvedený kód by mohl být přepsán takto:

constexpr int GiveFive () { return 5 ;} int nějaká_hodnota [ GiveFive () + 7 ]; // vytvoří pole 12 celých čísel; povoleno v C++ 11

Toto klíčové slovo umožňuje kompilátoru pochopit a ověřit, že GiveFive vrací konstantu.

Použití constexpr ukládá velmi přísná omezení na akce funkce:

  1. taková funkce musí vrátit hodnotu;
  2. tělo funkce musí mít tvar return expression ;
  3. výraz se musí skládat z konstant a/nebo volání jiných funkcí constexpr ;
  4. funkci označenou constexpr nelze použít, dokud není definována v aktuální kompilační jednotce.

V předchozí verzi standardu bylo možné v konstantních výrazech používat pouze proměnné typu integer nebo enum. V C++11 je toto omezení zrušeno pro proměnné, jejichž definici předchází klíčové slovo constexpr:

constexpr double zrychleniOfGravity = 9,8 ; constexpr double moonGravity = akceleraceOfGravity / 6 ;

Takové proměnné jsou již implicitně považovány za označené klíčovým slovem const . Mohou obsahovat pouze výsledky konstantních výrazů nebo konstruktory takových výrazů.

Pokud je nutné zkonstruovat konstantní hodnoty z uživatelsky definovaných typů, konstruktory takových typů lze deklarovat také pomocí constexpr . Konstruktor konstantního výrazu, stejně jako konstantní funkce, musí být také definován před prvním použitím v aktuální kompilační jednotce. Takový konstruktor musí mít prázdné tělo a takový konstruktor musí inicializovat členy svého typu pouze s konstantami.

Změny v definici jednoduchých dat

Ve standardním C++ lze pouze struktury, které splňují určitou sadu pravidel, považovat za prostý starý datový typ ( POD). Existují dobré důvody očekávat, že tato pravidla budou rozšířena tak, aby bylo více typů považováno za PODy. Typy, které splňují tato pravidla, lze použít v implementaci objektové vrstvy kompatibilní s C. Seznam těchto pravidel v C++03 je však příliš omezující.

C++11 uvolní několik pravidel týkajících se definice jednoduchých datových typů.

Třída je považována za jednoduchý datový typ, pokud je triviální , má standardní rozložení ( standard-layout ) a pokud jsou typy všech jejích nestatických datových členů také jednoduché datové typy.

Triviální třída je třída, která:

  1. obsahuje triviální výchozí konstruktor,
  2. neobsahuje netriviální kopírovací konstruktory,
  3. neobsahuje netriviální konstruktory pohybu,
  4. neobsahuje netriviální operátory přiřazení kopie,
  5. neobsahuje netriviální operátory přiřazení pohybu,
  6. obsahuje triviální destruktor.

Třída se standardním umístěním je třída, která:

  1. neobsahuje nestatické datové členy typu vlastní třídy (nebo pole prvků tohoto typu) nebo referenční typ,
  2. neobsahuje virtuální funkce,
  3. neobsahuje virtuální základní třídy,
  4. má stejný typ přístupnosti ( public, private, protected) pro všechny nestatické datové členy,
  5. nemá základní třídy s nestandardním umístěním,
  6. není třída, která současně obsahuje zděděné a nezděděné nestatické datové členy nebo obsahuje nestatické datové členy zděděné z několika základních tříd najednou,
  7. nemá základní třídy stejného typu jako první nestatický datový člen (pokud existuje).

Urychlit kompilaci

Externí šablony

Ve standardním C++ musí kompilátor vytvořit instanci šablony, kdykoli narazí na svou plnou specializaci v překladové jednotce. To může výrazně prodloužit dobu kompilace, zvláště když je šablona konkretizována se stejnými parametry ve velkém počtu překladových jednotek. V současné době neexistuje způsob, jak říci C++, že by neměla existovat žádná instance.

C++11 představil myšlenku externích šablon. C++ již má syntaxi, která říká kompilátoru, že šablona by měla být vytvořena v určitém okamžiku:

šablona class std :: vector < MyClass > ;

C++ postrádá schopnost zabránit kompilátoru vytvořit instanci šablony v překladové jednotce. C++ 11 jednoduše rozšiřuje tuto syntaxi:

extern template class std :: vector < MyClass > ;

Tento výraz říká kompilátoru , aby nevytvářel instanci šablony v této překladové jednotce.

Vylepšená použitelnost

Tyto funkce mají usnadnit používání jazyka. Umožňují posílit bezpečnost typů, minimalizovat duplicitu kódu, ztížit zneužití kódu a tak dále.

Inicializační seznamy

Koncept inicializačních seznamů přišel do C++ z C. Myšlenka je taková, že strukturu nebo pole lze vytvořit předáním seznamu argumentů ve stejném pořadí, v jakém jsou definováni členové struktury. Inicializační seznamy jsou rekurzivní, což umožňuje jejich použití pro pole struktur a struktury obsahující vnořené struktury.

struct objekt { plavat první ; int druhý ; }; Objekt skalární = { 0,43f , 10 }; // jeden objekt, s first=0.43f a second=10 Object anArray [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // pole tří objektů

Inicializační seznamy jsou velmi užitečné pro statické seznamy a když chcete inicializovat strukturu na konkrétní hodnotu. C++ také obsahuje konstruktory, které mohou obsahovat obecnou práci inicializace objektů. Standard C++ umožňuje použití inicializačních seznamů pro struktury a třídy za předpokladu, že odpovídají definici Plain Old Data (POD). Třídy jiné než PODD nemohou k inicializaci používat inicializační seznamy, včetně standardních kontejnerů C++, jako jsou vektory.

C++11 spojil koncept inicializačních seznamů a šablony třídy s názvem std::initializer_list . To umožnilo konstruktérům a dalším funkcím přijímat inicializační seznamy jako parametry. Například:

třída SequenceClass { veřejnost : SequenceClass ( std :: seznam_inicializátoru < int > seznam ); };

Tento popis vám umožňuje vytvořit SequenceClass z posloupnosti celých čísel následovně:

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

To ukazuje, jak funguje speciální druh konstruktoru pro inicializační seznam. S třídami obsahujícími takové konstruktory se během inicializace zachází zvláštním způsobem (viz níže ).

Třída std::initializer_list<> je definována ve standardní knihovně C++11. Objekty této třídy však mohou být staticky vytvořeny pouze kompilátorem C++11 pomocí syntaxe hranatých závorek {}. Seznam lze po vytvoření zkopírovat, bude to však kopírování po odkazu. Inicializační seznam je konstantní: po vytvoření nelze měnit jeho členy ani jejich data.

Protože std::initializer_list<> je plnohodnotný typ, lze jej použít ve více než jen konstruktorech. Běžné funkce mohou jako argument převzít zadané inicializační seznamy, například:

void NázevFunkce ( std :: seznam_inicializátoru < float > seznam ); Název funkce ({ 1.0f , -3.45f , -0.4f });

Standardní kontejnery lze inicializovat takto:

std :: vector < std :: string > v = { "xyzzy" , "plugh" , "abracadabra" }; std :: vector < std :: string > v { "xyzzy" , "plugh" , "abracadabra" }; Obecná inicializace

Standard C++ obsahuje řadu problémů souvisejících s inicializací typu. Existuje několik způsobů inicializace typů a ne všechny vedou ke stejným výsledkům. Například tradiční syntaxe inicializačního konstruktoru může vypadat jako deklarace funkce a je třeba věnovat zvláštní pozornost tomu, aby ji kompilátor nezanalyzoval špatně. SomeType var = {/*stuff*/};Pomocí agregačních inicializátorů (druhu ) lze inicializovat pouze agregační typy a typy POD .

C++11 poskytuje syntaxi, která umožňuje použití jediné formy inicializace pro všechny druhy objektů rozšířením syntaxe inicializačního seznamu:

struct BasicStruct { int x ; dvojité y ; }; struct AltStruct { AltStruct ( int x , double y ) : x_ ( x ), y_ ( y ) {} soukromý : int x_ ; dvojité y_ ; }; BasicStruct var1 { 5 , 3.2 }; AltStruct var2 { 2 , 4,3 };

Inicializace var1 funguje úplně stejně jako inicializace agregátů, to znamená, že každý objekt bude inicializován zkopírováním odpovídající hodnoty z inicializačního seznamu. V případě potřeby bude použita implicitní konverze typu. Pokud požadovaná transformace neexistuje, bude zdrojový kód považován za neplatný. Během inicializace var2 bude zavolán konstruktor.

Je možné napsat kód takto:

struct IdString { std :: stringname ; _ int identifikátor ; }; IdString GetString () { return { "NějakéJméno" , 4 }; // Všimněte si nedostatku explicitních typů }

Obecná inicializace zcela nenahrazuje syntaxi inicializace konstruktoru. Pokud má třída konstruktor, který jako argument bere inicializační seznam ( TypeName(initializer_list<SomeType>); ), bude mít přednost před ostatními možnostmi vytváření objektů. Například v C++11 std::vector obsahuje konstruktor, který jako argument bere inicializační seznam:

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

Tento kód bude mít za následek volání konstruktoru, které vezme jako argument inicializační seznam, spíše než konstruktor s jedním parametrem, který vytvoří kontejner dané velikosti. K volání tohoto konstruktoru bude uživatel muset použít standardní syntaxi vyvolání konstruktoru.

Typový závěr

Ve standardním C++ (a C) musí být typ proměnné výslovně specifikován. S příchodem typů šablon a technik metaprogramování šablon však nelze typ některých hodnot, zejména návratových hodnot funkcí, snadno určit. To vede k potížím při ukládání mezilehlých dat do proměnných, někdy může být nutné znát vnitřní strukturu konkrétní metaprogramovací knihovny.

C++11 nabízí dva způsoby, jak tyto problémy zmírnit. Za prvé, definice explicitně inicializovatelné proměnné může obsahovat klíčové slovo auto . Výsledkem bude vytvoření proměnné typu inicializační hodnoty:

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

Typ someStrangeCallableType se stane typem, který konkrétní implementace funkce šablony vrátí std::bindpro dané argumenty. Tento typ bude snadno určen kompilátorem během sémantické analýzy, ale programátor by musel provést nějaký průzkum, aby typ určil.

Typ otherVariable je také dobře definovaný, ale může být stejně snadno definován programátorem. Tento typ je int , stejný jako celočíselná konstanta.

Kromě toho lze klíčové slovo decltype použít k určení typu výrazu v době kompilace . Například:

int someInt ; decltype ( someInt ) otherIntegerVariable = 5 ;

Použití decltype je nejužitečnější ve spojení s auto , protože typ proměnné deklarované jako auto zná pouze kompilátor. Použití decltype může být také docela užitečné ve výrazech, které používají přetěžování operátorů a specializaci šablon.

autolze také použít ke snížení redundance kódu. Například místo:

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

programátor umí napsat:

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

Rozdíl je zvláště patrný, když programátor používá velké množství různých kontejnerů, i když stále existuje dobrý způsob, jak snížit nadbytečný kód - pomocí typedef.

Typ označený decltype se může lišit od typu odvozeného pomocí auto .

#include <vektor> int main () { const std :: vector < int > v ( 1 ); auto a = v [ 0 ]; // typ a - int decltype ( v [ 0 ]) b = 1 ; // typ b - const int& (návratová hodnota // std::vector<int>::operator[](typ_velikosti) const) auto c = 0 ; // napište c - int auto d = c ; // zadejte d - int decltype ( c ) e ; // typ e - int, typ entity s názvem c decltype (( c )) f = c ; // typ f je int& protože (c) je lvalue decltype ( 0 ) g ; // typ g je int, protože 0 je rvalue } Pro-smyčka přes kolekci

Ve standardním C++ vyžaduje iterace prvků kolekce hodně kódu . Některé jazyky, jako je C# , mají prostředky, které poskytují příkaz „ foreach , který automaticky prochází prvky kolekce od začátku do konce. C++11 zavádí podobnou funkci. Příkaz for usnadňuje iteraci sady prvků:

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

Tato forma for, anglicky nazývaná „range-based for“, navštíví každý prvek kolekce. To bude platit pro pole C , seznamy inicializátorů a jakékoli další typy , které mají funkce begin()a end()které vracejí iterátory . Všechny kontejnery ve standardní knihovně , které mají pár začátek/konec, budou fungovat s příkazem for v kolekci.

Takový cyklus bude fungovat také například s poli typu C, protože C++11 pro ně uměle zavádí potřebné pseudometody (začátek, konec a některé další).

// rozsahově založené procházení klasického pole int arr1 [] = { 1 , 2 , 3 }; for ( auto el : arr1 ); Lambda funkce a výrazy

Ve standardním C++, například při použití standardních algoritmů knihovny C++ sort and find , je často potřeba definovat predikátové funkce poblíž místa, kde je algoritmus volán. V jazyce pro to existuje pouze jeden mechanismus: schopnost definovat třídu funktoru (předávání instance třídy definované uvnitř funkce algoritmům je zakázáno (Meyers, Effective STL)). Tato metoda je často příliš nadbytečná a podrobná a pouze znesnadňuje čtení kódu. Standardní pravidla C++ pro třídy definované ve funkcích navíc neumožňují jejich použití v šablonách a znemožňují tak jejich použití.

Zřejmým řešením problému bylo umožnit definici výrazů lambda a funkcí lambda v C++11. Funkce lambda je definována takto:

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

Návratový typ této nepojmenované funkce se vypočítá jako decltype(x+y) . Návratový typ lze vynechat pouze v případě, že funkce lambda má tvar . To omezuje velikost funkce lambda na jeden výraz. return expression

Návratový typ lze zadat explicitně, například:

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

Tento příklad vytvoří dočasnou proměnnou z pro uložení mezilehlé hodnoty. Stejně jako u normálních funkcí není tato mezihodnota mezi voláními zachována.

Návratový typ lze zcela vynechat, pokud funkce nevrací hodnotu (tj. návratový typ je void )

Je také možné použít odkazy na proměnné definované ve stejném rozsahu jako funkce lambda. Soubor takových proměnných se obvykle nazývá uzávěr . Uzávěry jsou definovány a používány následovně:

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

Tím se zobrazí součet všech prvků v seznamu. Celková proměnná je uložena jako součást uzavření funkce lambda. Protože odkazuje na proměnnou zásobníku total , může změnit její hodnotu.

Uzavírací proměnné pro lokální proměnné lze také definovat bez použití referenčního symbolu & , což znamená, že funkce zkopíruje hodnotu. To nutí uživatele deklarovat záměr odkazovat nebo kopírovat místní proměnnou.

Pro funkce lambda, u kterých je zaručeno, že se spouštějí ve svém rozsahu, je možné použít všechny proměnné zásobníku, aniž by bylo nutné na ně explicitně odkazovat:

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

Metody implementace se mohou interně lišit, ale očekává se, že funkce lambda bude ukládat ukazatel na zásobník funkce, ve kterém byla vytvořena, spíše než pracovat s odkazy na jednotlivé proměnné zásobníku.

[&]Pokud je místo toho [=]použito , budou zkopírovány všechny použité proměnné, což umožní použití funkce lambda mimo rozsah původních proměnných.

Výchozí způsob převodu lze také doplnit o seznam jednotlivých proměnných. Pokud například potřebujete předávat většinu proměnných odkazem a jednu hodnotu, můžete použít následující konstrukci:

int celkem = 0 ; int hodnota = 5 ; [ & , hodnota ]( int x ) { total += ( x * hodnota ); } ( 1 ); //(1) volání funkce lambda s hodnotou 1

To způsobí, že součet bude předán odkazem a hodnota hodnotou.

Pokud je funkce lambda definována v metodě třídy, je považována za přítele této třídy. Takové funkce lambda mohou používat odkaz na objekt typu třídy a přistupovat k jeho vnitřním polím:

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

To bude fungovat pouze v případě, že rozsah funkce lambda je metoda třídy SomeType .

Speciálním způsobem je implementována práce s ukazatelem this na objekt, se kterým aktuální metoda interaguje. Musí být výslovně označen ve funkci lambda:

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

Pomocí formuláře [&]nebo [=]funkce lambda to zpřístupníte automaticky.

Typ funkcí lambda je závislý na implementaci; název tohoto typu je dostupný pouze kompilátoru. Pokud potřebujete předat funkci lambda jako parametr, musí to být typ šablony nebo uložený pomocí funkce std::function . Klíčové slovo auto vám umožňuje uložit funkci lambda lokálně:

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

Kromě toho, pokud funkce nebere žádné argumenty, ()můžete vynechat:

auto myLambdaFunc = []{ std :: cout << "ahoj" << std :: endl ; }; Syntaxe alternativní funkce

Někdy je potřeba implementovat šablonu funkce, která by vedla k výrazu, který má stejný typ a stejnou kategorii hodnot jako nějaký jiný výraz.

šablona < typename LHS , typename RHS > RETURN_TYPE AddingFunc ( const LHS & lhs , const RHS & rhs ) // jaký by měl být RETURN_TYPE? { return lhs + rhs ; }

Aby měl výraz AddingFunc(x, y) stejný typ a stejnou kategorii hodnoty jako výraz lhs + rhs , když jsou zadány argumenty x a y , lze v C++11 použít následující definici:

template < typename LHS , typename RHS > decltype ( std :: declval < const LHS &> () + std :: declval < const RHS &> ()) AddingFunc ( const LHS & lhs , const RHS & rhs ) { return lhs + rhs ; }

Tento zápis je poněkud těžkopádný a bylo by hezké mít možnost používat lhs a rhs místo std::declval<const LHS &>() a std::declval<const RHS &>(). Nicméně v další verzi

template < typename LHS , typename RHS > decltype ( lhs + rhs ) AddingFunc ( const LHS & lhs , const RHS & rhs ) // Neplatí v C++11 { return lhs + rhs ; }

identifikátory lhs a rhs použité v operandu decltype, které jsou lépe čitelné pro člověka, nemohou označovat volby deklarované později. K vyřešení tohoto problému C++11 zavádí novou syntaxi pro deklarování funkcí s návratovým typem na konci:

template < typename LHS , typename RHS > auto AddingFunc ( const LHS & lhs , const RHS & rhs ) -> decltype ( lhs + rhs ) { return lhs + rhs ; }

Je však třeba poznamenat, že v obecnější implementaci AddingFunc níže nová syntaxe netěží ze stručnosti:

šablona < typename LHS , typename RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: vpřed < LHS > ( lhs ) + std :: vpřed < RHS > ( rhs )) { return std :: vpřed < LHS > ( lhs ) + std :: vpřed < RHS > ( rhs ); } šablona < typename LHS , typename RHS > auto AddingFunc ( LHS && lhs , RHS && rhs ) -> decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // stejný efekt jako u std::forward výše { return std :: vpřed < LHS > ( lhs ) + std :: vpřed < RHS > ( rhs ); } template < typename LHS , typename RHS > decltype ( std :: declval < LHS > () + std :: declval < RHS > ()) // stejný efekt jako uvedení typu na konec AddingFunc ( LHS && lhs , RHS && rhs ) { return std :: vpřed < LHS > ( lhs ) + std :: vpřed < RHS > ( rhs ); }

Novou syntaxi lze použít v jednodušších deklaracích a deklaracích:

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

Použití klíčového slova " " autov tomto případě znamená pouze pozdní označení návratového typu a nesouvisí s jeho automatickým vyvozováním.

Vylepšení konstruktorů objektů

Standardní C++ neumožňuje, aby byl jeden konstruktor třídy volán z jiného konstruktoru stejné třídy; každý konstruktor musí plně inicializovat všechny členy třídy nebo k tomu volat metody třídy. Nekonstantní členy třídy nelze inicializovat v místě, kde jsou tyto členy deklarovány.

C++11 se těchto problémů zbaví.

Nový standard umožňuje volat jeden konstruktor třídy z jiného (tzv. delegování). To vám umožňuje psát konstruktory, které používají chování jiných konstruktorů, aniž byste zaváděli duplicitní kód.

Příklad:

class SomeType { int číslo ; veřejnost : SomeType ( int new_number ) : number ( new_number ) {} SomeType () : SomeType ( 42 ) {} };

Z příkladu můžete vidět, že konstruktor SomeTypebez argumentů volá konstruktor stejné třídy s celočíselným argumentem pro inicializaci proměnné number. Podobného efektu lze dosáhnout zadáním počáteční hodnoty 42 pro tuto proměnnou hned při její deklaraci.

class SomeType { int číslo = 42 ; veřejnost : SomeType () {} explicitní SomeType ( int new_number ) : number ( new_number ) {} };

Jakýkoli konstruktor třídy se inicializuje numberna 42, pokud mu sám nepřiřadí jinou hodnotu.

Java , C# a D jsou příklady jazyků, které také řeší tyto problémy .

Je třeba poznamenat, že pokud je v C++03 objekt považován za plně vytvořený, když jeho konstruktor dokončí provádění, pak v C++11 po provedení alespoň jednoho delegujícího konstruktoru budou ostatní konstruktory pracovat na plně stavěný objekt. Navzdory tomu budou objekty odvozené třídy vytvořeny až poté, co budou provedeny všechny konstruktory základních tříd.

Explicitní náhrada virtuálních funkcí a konečnost

Je možné, že signatura virtuální metody byla změněna v základní třídě nebo byla původně nesprávně nastavena v odvozené třídě. V takových případech daná metoda v odvozené třídě nepřepíše odpovídající metodu v základní třídě. Pokud tedy programátor správně nezmění signaturu metody ve všech odvozených třídách, metoda nemusí být během provádění programu správně volána. Například:

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

Zde je název virtuální funkce deklarovaný v odvozené třídě napsán chybně, takže taková funkce nepřepíše Base::some_func, a proto nebude volána polymorfně přes ukazatel nebo odkaz na základní podobjekt.

C++ 11 přidá možnost sledovat tyto problémy v době kompilace (spíše než v době běhu). Kvůli zpětné kompatibilitě je tato funkce volitelná. Nová syntaxe je uvedena níže:

struktura B { virtual void some_func (); virtuální void f ( int ); virtuální void g () const ; }; struktura D1 : veřejný B { void sone_func () override ; // chyba: neplatný název funkce void f ( int ) override ; // OK: přepíše stejnou funkci v základní třídě virtual void f ( long ) override ; // chyba: neshoda typu parametru virtual void f ( int ) const override ; // chyba: neshoda funkce cv-kvalifikace virtual int f ( int ) override ; // chyba: neshoda návratového typu virtual void g () const final ; // OK: přepíše stejnou funkci v základní třídě virtual void g ( long ); // OK: nová virtuální funkce }; struktura D2 : D1 { virtuální void g () const ; // chyba: pokus o nahrazení finální funkce };

Přítomnost specifikátoru pro virtuální funkci finalznamená, že její další nahrazení je nemožné. Třída definovaná s konečným specifikátorem také nemůže být použita jako základní třída:

struktura F konečná { int x , y ; }; struct D : F // chyba: dědění z finálních tříd není povoleno { int z ; };

Identifikátory overridea finalmají zvláštní význam pouze při použití v určitých situacích. V jiných případech je lze použít jako běžné identifikátory (například jako název proměnné nebo funkce).

Nulová konstanta ukazatele

Od příchodu C v roce 1972 hraje konstanta 0 dvojí roli celého čísla a nulového ukazatele. Jedním ze způsobů, jak se vypořádat s touto nejednoznačností, která je vlastní jazyku C, je makro NULL, které obvykle provádí substituci ((void*)0)nebo 0. C++ se v tomto ohledu od C liší a umožňuje pouze použití 0nulového ukazatele jako konstanty. To vede ke špatné interakci s přetížením funkcí:

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

Pokud je makro NULLdefinováno jako (což je běžné v C++), výsledkem 0řádku bude volání , ne jak by rychlý pohled na kód mohl naznačovat, což téměř jistě není to, co programátor zamýšlel. foo(NULL);foo(int)foo(char *)

Jednou z novinek C++11 je nové klíčové slovo pro popis konstanty nulového ukazatele - nullptr. Tato konstanta je typu std::nullptr_t, kterou lze implicitně převést na typ libovolného ukazatele a porovnat s jakýmkoli ukazatelem. Implicitní převod na integrální typ není povolen, s výjimkou bool. Původní návrh standardu neumožňoval implicitní konverzi na boolean, ale skupina pro tvorbu standardů takové konverze umožňovala kvůli kompatibilitě s konvenčními typy ukazatelů. Navrhované znění bylo změněno po jednomyslném hlasování v červnu 2008 [1] .

Pro zpětnou kompatibilitu lze konstantu 0použít také jako nulový ukazatel.

char * pc = nullptr ; // true int * pi = nullptr ; // true bool b = nullptr ; // že jo. b=nepravda. int i = nullptr ; // chyba foo ( nullptr ); // volá foo(char *), ne foo(int);

Konstrukce, kde je zaručeno, že je ukazatel prázdný, jsou často jednodušší a bezpečnější než ostatní – takže je můžete přetížit . nullptr_t

třída Užitečné zatížení ; třída SmartPtr { SmartPtr () = výchozí ; SmartPtr ( nullptr_t ) {} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< explicitní SmartPtr ( Payload * aData ) : fData ( aData ) {} // kopírování konstruktorů a op= vynechání ~ SmartPtr () { delete fData ; } soukromý : Užitečná zátěž * fData = nullptr ; } SmartPtr getPayload1 () { return nullptr ; } // Bude zavoláno přetížení SmartPtr(nullptr_t). Silně typované výčty

Ve standardním C++ nejsou výčty typově bezpečné. Ve skutečnosti jsou reprezentovány celými čísly, a to navzdory skutečnosti, že samotné typy výčtů se od sebe liší. To umožňuje provádět srovnání mezi dvěma hodnotami z různých výčtů. Jedinou možností, kterou C++03 nabízí k ochraně výčtů, je implicitně nepřevádět celá čísla nebo prvky jednoho výčtu na prvky jiného výčtu. Také způsob, jakým je reprezentován v paměti (typ celé číslo), je závislý na implementaci, a proto není přenosný. A konečně, prvky výčtu mají společný rozsah, což znemožňuje vytvářet prvky se stejným názvem v různých výčtech.

C++11 nabízí speciální klasifikaci těchto výčtů, bez výše uvedených nevýhod. K popisu takových výčtů se používá deklarace enum class(může být také použita enum structjako synonymum):

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

Takový výčet je typově bezpečný. Prvky výčtu třídy nelze implicitně převést na celá čísla. V důsledku toho je také nemožné srovnání s celými čísly (výraz Enumeration::Val4 == 101má za následek chybu kompilace).

Typ výčtu třídy je nyní nezávislý na implementaci. Ve výchozím nastavení, stejně jako v případě výše, je tento typ int, ale v ostatních případech lze typ nastavit ručně takto:

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

Rozsah členů výčtu je určen rozsahem názvu výčtu. Použití názvů prvků vyžaduje zadání názvu výčtu třídy. Takže například hodnota Enum2::Val1je definována, ale hodnota Val1 není definována.

C++11 navíc nabízí možnost explicitně určit rozsah a základní typy pro běžné výčty:

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

V tomto příkladu jsou názvy prvků výčtu definovány v prostoru výčtu (Enum3::Val1), ale kvůli zpětné kompatibilitě jsou názvy prvků dostupné také ve společném oboru.

Také v C++11 je možné předem deklarovat výčty. V předchozích verzích C++ to nebylo možné, protože velikost výčtu závisela na jeho prvcích. Takové deklarace lze použít pouze v případě, že je specifikována velikost výčtu (explicitně nebo implicitně):

enum Enum1 ; // neplatné pro C++ a C++11; základní typ nelze určit enum Enum2 : unsigned int ; // true pro C++11, základní typ explicitně specifikovaný enum class Enum3 ; // true pro C++11, základní typ je int enum class Enum4 : unsigned int ; // platí pro C++11. enum Enum2 : unsigned short ; // neplatný pro C++11, protože Enum2 byl dříve deklarován s jiným základním typem Lomené závorky

Standardní analyzátory C++ vždy definují kombinaci znaků ">>" jako operátor posunu vpravo. Absence mezery mezi uzavíracími lomenými závorkami v parametrech šablony (pokud jsou vnořené) je považována za chybu syntaxe.

C++11 v tomto případě vylepšuje chování analyzátoru tak, že více pravoúhlých závorek bude interpretováno jako uzavírací seznamy argumentů šablony.

Popsané chování lze opravit ve prospěch starého přístupu pomocí závorek.

šablona < class T > class Y { /* ... */ }; Y < X < 1 >> x3 ; // Správně, stejně jako "Y<X<1> > x3;". Y < X < 6 >> 1 >> x4 ; // Chyba syntaxe. Musíte napsat "Y<X<(6>>1)>> x4;".

Jak je uvedeno výše, tato změna není zcela kompatibilní s předchozím standardem.

Explicitní převodní operátory

Standard C++ poskytuje klíčové slovo explicitjako modifikátor pro jednoparametrové konstruktory, takže takové konstruktory nefungují jako implicitní konverzní konstruktory. To však nijak neovlivňuje skutečné operátory konverze. Třída inteligentního ukazatele může například obsahovat operator bool()napodobování běžného ukazatele. Takový operátor lze volat například takto: if(smart_ptr_variable)(větvení se provede, pokud ukazatel není null). Problém je, že takový operátor nechrání před dalšími neočekávanými konverzemi. Vzhledem k tomu, že typ boolje deklarován jako aritmetický typ v C++, je možný implicitní převod na jakýkoli typ celého čísla nebo dokonce na typ s plovoucí desetinnou čárkou, což může vést k neočekávaným matematickým operacím.

V C++11 se klíčové slovo explicitvztahuje také na konverzní operátory. Stejně jako konstruktory chrání před neočekávanými implicitními konverzemi. Nicméně situace, kdy jazyk kontextově očekává booleovský typ (například v podmíněných výrazech, cyklech a operandech logických operátorů), jsou považovány za explicitní převody a operátor explicitního boolovského převodu je vyvolán přímo.

Typ šablony

Ve standardním C++ lze klíčové slovo typedefpoužít pouze jako definici synonyma pro jiný typ, včetně synonyma pro specifikaci šablony se všemi jejími parametry. Není však možné vytvořit synonymum šablony. Například:

template < typename First , typename Second , int third > třída SomeType ; šablona < typenameSecond > _ typedef SomeType < OtherType , Second , 5 > TypedefName ; // Není možné v C++

Toto se nezkompiluje.

C++11 přidal tuto schopnost s následující syntaxí:

template < typename First , typename Second , int third > třída SomeType ; šablona < typenameSecond > _ using TypedefName = SomeType < OtherType , Second , 5 > ;

V C++11 lze direktivu usingpoužít také jako alias datového typu.

typedef void ( * OtherType )( double ); // Starý styl pomocí OtherType = void ( * )( double ); // Nová syntaxe Odstranění omezení z unie

V předchozích standardech C++ existuje řada omezení pro použití členů typů tříd v rámci svazů. Sjednocení zejména nemůže obsahovat objekty s netriviálním konstruktorem. C++11 některá z těchto omezení odstraňuje. [2]

Zde je jednoduchý příklad spojení, které je povoleno v C++ 11:

//pro umístění nové #include <nové> structPoint { _ Bod () {} Bod ( int x , int y ) : x_ ( x ), y_ ( y ) {} int x_ , y_ ; }; unie U { int z ; dvojité w ; Bod p ; // Neplatí pro C++03, protože Point má netriviální konstruktor. Kód však funguje správně v C++11. U () { nový ( & p ) Bod (); } // Pro sjednocení nejsou definovány žádné netriviální metody. // V případě potřeby je lze odstranit, aby ruční definice fungovala };

Změny nemají vliv na stávající kód, protože pouze uvolňují stávající omezení.

Rozšíření funkčnosti

Tato část popisuje nové funkce, které dříve nebyly dostupné nebo vyžadovaly speciální nepřenosné knihovny.

Šablony proměnných argumentů

Před C++ 11 mohly šablony (tříd nebo funkcí) mít pouze nastavený počet argumentů, definovaných při původní deklaraci šablony. C++11 umožňuje definovat šablony s proměnným počtem argumentů libovolného typu.

šablona < typename ... Hodnoty > class tuple ;

Například třída šablony tuple ( tuple ) akceptuje libovolný počet názvů typů jako parametry šablony:

class n-tice < int , std :: vektor < int > , std :: mapa < std :: řetězec , std :: vektor < int >>> jméno_některé_instance ;

Argumenty mohou chybět, takže možnost class tuple<> some_instance_namebude také fungovat.

Chcete-li zabránit vytváření instance šablony bez argumentů, lze použít následující definici:

template < typename First , typename ... Rest > class tuple ;

Šablony proměnných argumentů jsou také použitelné pro funkce, což umožňuje jejich použití v typově bezpečných variantách variadických funkcí (jako je printf) a pro manipulaci s netriviálními objekty.

template < typename ... Params > void printf ( const std :: string & str_format , Params ... parametry );

Operátor ... zde hraje dvě role. Nalevo od Params operátor oznamuje potřebu zabalit parametry. Použití sbalených parametrů umožňuje přidružit 0 nebo více argumentů k šabloně. Zabalené parametry lze použít pro více než jen předávání názvů typů. Operátor ... vpravo zase rozbalí parametry do samostatných argumentů (viz args...tělo funkce v příkladu níže).

Je také možné rekurzivně používat šablony s proměnným počtem argumentů. Jedním příkladem by mohla být typově bezpečná náhrada za printf :

void printf ( const char * s ) { zatímco ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) throw std :: runtime_error ( "neplatný formátovací řetězec: chybějící argumenty" ); std :: cout << * s ++ ; } } template < typename T , typename ... Args > void printf ( const char * s , T hodnota , Args ... args ) { zatímco ( * s ) { if ( * s == '%' && * ( ++ s ) != '%' ) { std :: cout << hodnota ; ++ s ; printf ( s , args ...); // pokračovat ve zpracování argumentů, i když *s == 0 return ; } std :: cout << * s ++ ; } throw std :: logic_error ( "další argumenty poskytnuté printf" ); }

Tento vzorec je rekurzivní. Všimněte si, že funkce printf volá výsledky konkretizace sebe sama nebo základní funkci printf, pokud je args... prázdný.

Neexistuje žádný snadný způsob, jak obejít parametry v variadic šabloně. Navzdory tomu použití argumentu operátor rozbalení tento problém obchází.

Třída může být definována například takto:

template < typename ... BaseClasses > class ClassName : public BaseClasses ... { veřejnost : ClassName ( BaseClasses && ... base_classes ) : BaseClasses ( base_classes )... {} };

Operátor rozbalení zduplikuje všechny typy nadřazených tříd ClassNametakovým způsobem, že třída bude zděděna ze všech typů zadaných v parametrech šablony. Kromě toho musí konstruktor přijmout odkaz na všechny základní třídy, aby byla inicializována každá nadřazená základní třída ClassName.

Parametry šablony lze přesměrovat. V kombinaci s referencemi rvalue (viz výše) můžete přesměrovat:

template < typename TypeToConstruct > struct SharedPtrAllocator { template < typename ... Args > std :: shared_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( new TypeToConstruct ( std :: forward < Args > ( params )...)); }; };

Tento kód rozbalí seznam argumentů do konstruktoru TypeToConstruct. Syntaxe std::forward<Args>(params)vám umožňuje naprosto transparentně přesměrovat argumenty do konstruktoru, bez ohledu na jejich rvalue povahu. Funkce automaticky zabalí ukazatele, std::shared_ptraby byla zajištěna ochrana před úniky paměti.

Je také možné určit počet zabalených argumentů následovně:

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

Zde SomeStruct<Type1, Type2>::sizese rovná 2 a SomeStruct<>::sizerovná se 0.

Nové řetězcové literály

C++03 nabízel dva typy řetězcových literálů. První typ, řetězec s dvojitými uvozovkami, je pole typu const char. Druhý typ, definovaný jako L"", je nulou ukončené pole typu const wchar_t, kde wchar_tje široký charakter neurčitých velikostí a sémantiky. Žádný z typů literálů není určen k podpoře řetězcových literálů UTF-8 , UTF-16 nebo jakéhokoli jiného typu kódování Unicode

Definice typu charbyla upravena tak, aby výslovně říkala, že je to alespoň velikost potřebná k uložení osmibitového kódování UTF-8 a dostatečně velká, aby obsahovala jakýkoli znak v běhové znakové sadě. Dříve ve standardu byl tento typ definován jako jeden znak, později, po standardu jazyka C, bylo zaručeno, že zabírá alespoň 8 bitů.

Standard C++11 podporuje tři kódování Unicode: UTF-8 , UTF-16 a UTF-32 . Kromě výše uvedených změn vestavěného typu znaků charpřidává C++11 dva nové typy znaků: char16_ta char32_t. Jsou navrženy pro ukládání znaků UTF-16 a UTF-32.

Následující text ukazuje, jak vytvořit řetězcové literály pro každé z těchto kódování:

u8 "Jsem řetězec UTF-8." u "Toto je řetězec UTF-16." U "Toto je řetězec UTF-32."

Typ první řady je normální const char[]. Typ druhého řádku je const char16_t[]. Typ třetího řádku je const char32_t[].

Při vytváření řetězcových literálů ve standardu Unicode je často užitečné vložit kód Unicode přímo do řetězce. C++ 11 pro to poskytuje následující syntaxi:

u8 "Toto je znak Unicode: \u2018 ." u "Toto je větší znak Unicode: \u2018 ." U "Toto je znak Unicode: \U00002018 ."

Číslo za \umusí být šestnáctkové; není třeba používat předponu 0x. Identifikátor \uznamená 16bitový kód Unicode; pro zadání 32bitového kódu \Use také používá 32bitové hexadecimální číslo. Lze zadat pouze platné kódy Unicode. Například kódy v rozsahu U+D800-U+DFFF nejsou povoleny, protože jsou vyhrazeny pro náhradní páry UTF-16.

Někdy je také užitečné vyhnout se ručnímu escapování řetězců, zejména při použití literálů souboru XML , skriptovacích jazyků nebo regulárních výrazů. Pro tyto účely C++ 11 podporuje „raw“ řetězcové literály:

R"(Data řetězce \ Věci " )" R"oddělovač(data řetězce \ Stuff ")oddělovač"

V prvním případě je vše mezi "(a )"součástí řetězce. Postavy "a \není třeba uniknout. Ve druhém případě "delimiter(začíná řetězec a končí pouze tehdy, když dosáhne )delimiter". Řetězec delimitermůže být libovolný řetězec o délce až 16 znaků, včetně prázdného řetězce. Tento řetězec nemůže obsahovat mezery, řídicí znaky, ' (', ' )' nebo znak ' \'. Použití tohoto oddělovacího řetězce umožňuje použití znaku ' )' v nezpracovaných řetězcových literálech. Například R"delimiter((a-z))delimiter"je ekvivalentní "(a-z)"[3] .

"Raw" řetězcové literály lze kombinovat s rozšířeným souborovým literálem (prefix L"") nebo libovolnými předponami literálu Unicode.

LR"(Raw široký řetězcový literál \t (bez tabulátoru))" u8R"XXX(Jsem "raw UTF-8" řetězec.)XXX" uR"*(Toto je "raw UTF-16" řetězec.)*" UR"(Toto je "raw UTF-32" řetězec.)" Vlastní literály

Vlastní literály jsou implementovány pomocí přetížení operátorů operator"". Literály mohou být inline nebo constexpr kvalifikátory . Je žádoucí, aby doslovný text začínal znakem podtržení, protože by mohlo dojít ke konfliktu s budoucími standardy. Například literál i již patří ke komplexním číslům z std::complex.

Literály mohou mít pouze jeden z následujících typů: const char * , unsigned long long int , long double , char , wchar_t , char16_t , char32_t. Literál stačí přetížit pouze pro typ const char * . Pokud nebude nalezen žádný vhodnější kandidát, bude zavolán operátor tohoto typu. Příklad převodu mil na kilometry:

constexpr int operátor "" _mi ( bez znaménka long long int i ) { return 1,6 * i ;}

Řetězcové literály mají druhý argument std::size_ta jeden z prvních: const char * , const wchar_t *, const char16_t * , const char32_t *. Řetězcové literály se vztahují na položky uzavřené ve dvojitých uvozovkách.

Vícevláknový paměťový model

C++11 standardizuje podporu pro vícevláknové programování. Zahrnují dvě části: paměťový model, který umožňuje koexistenci více vláken v programu, a knihovnu, která podporuje komunikaci mezi vlákny.

Paměťový model definuje, jak může více vláken přistupovat ke stejnému umístění paměti, a definuje, kdy se změny provedené jedním vláknem stanou viditelné pro ostatní vlákna.

Threaded storage Explicitní výchozí nastavení a odstranění speciálních metod

Místo těla metody lze zadat specifikátory a default.delete

třída Foo { veřejnost : foo () = výchozí ; Foo ( int x ) { /* ... */ } };

Specifikátor defaultznamená výchozí implementaci a lze jej použít pouze na speciální členské funkce:

  • výchozí konstruktor;
  • kopírovací konstruktor;
  • konstruktor pohybu;
  • operátor přiřazení;
  • operátor pohybu;
  • destruktor.

Specifikátor deleteoznačuje ty metody, se kterými nelze pracovat. Dříve jste museli takové konstruktory deklarovat v soukromém rozsahu třídy.

třída Foo { veřejnost : foo () = výchozí ; Foo ( const Foo & ) = smazat ; void bar ( int ) = smazat ; prázdný pruh ( dvojitý ) {} }; // ... Foo obj ; obj _ pruh ( 5 ); // chyba! obj _ bar ( 5,42 ); // OK Typ long long int

Typ integer long long intje specifikován v C99 a je de facto široce používán v C++. Nyní je oficiálně zařazen do standardu.

Statická diagnostika

C++11 má dva statické diagnostické mechanismy:

  • Pokud je výraz v závorkách nepravdivý , klíčové slovo static_assertvyvolá chybu kompilace.
  • Knihovna type_traitsobsahující šablony, které poskytují informace o typu v době kompilace.
#include <type_traits> šablona < classT > _ void run ( T * aData , size_t n ) { static_assert ( std :: is_pod < T >:: hodnota , "Typ T musí být jednoduchý." ); ... } Práce s velikostí datových členů ve třídách bez vytvoření objektu

C++03 umožňovalo použití operátoru sizeofna jednoduchých typech a objektech. Ale následující konstrukce byla neplatná:

struct SomeType { OtherType member ; }; sizeof ( SomeType :: member ); //Nefunguje v C++03, ale true v C++11.

Výsledkem tohoto volání by měla být velikost OtherType. C++03 takové volání nepodporuje a tento kód se nezkompiluje. C++11 takové konstrukce umožňuje.

Řízení zarovnání objektů a požadavky na zarovnání

C++11 umožňuje zarovnat proměnné pomocí operátorů alignofa alignas.

alignofvezme typ a vrátí počet bajtů, o které lze objekt posunout. Například struct X { int n; char c; };pro 8 bajtů alignofvrátí hodnotu 4. U odkazů vrátí hodnotu pro typ odkazu; pro pole hodnota prvku pole

alignasřídí zarovnání objektu v paměti. Můžete například určit, že pole znaků musí být správně zarovnáno, aby bylo možné uložit typ float:

alignas ( float ) unsigned char c [ sizeof ( float )] Povolení implementací s garbage collectorem Atributy

Změny ve standardní knihovně C++

Změny stávajících komponent

  • Při vkládání std::setprogramátor někdy ví, na jaké pozici nový prvek skončí. K tomu se používá volitelný parametr - "nápověda"; pokud je odhad správný, časový odhad bude amortizovaná konstanta, nikoli O(log n) . Význam „nápovědy“ v C++11 byl změněn: dříve to znamenalo prvek před aktuálním, což není úplně správné: není jasné, co dělat, pokud je vložení na první pozici. Nyní je to prvek po aktuálním.
  • Byla napsána pohodlná šablona, ​​která volá konstruktory bez alokace paměti - std::allocator_traits<>::construct(). Do všech kontejnerů byla přidána metoda, emplacekterá vytváří objekt na místě.
  • Přidány nové funkce jazyka C++11.
  • Přidané metody cbegina cendzaručené vytváření konst iterátorů. Pohodlné pro metaprogramování, pro nastavení typů pomocí auto.
  • V kontejnerech, které začínají paměť okrajem, se objevila funkce shrink_to_fit.
  • B std::listklade přísnější limity na to, co se děje v O ( n ), a co se děje v konstantním čase.
  • Přidán std::vectorpřímý přístup do paměti přes data().
  • Zakažte několika lidem std::stringodkazovat na stejnou vzpomínku. Díky tomu se objevil přímý přístup přes front(), což je výhodné například pro interakci stringu a WinAPI .

Řízení toku

Zatímco jazyk C++03 poskytuje paměťový model, který podporuje multithreading, hlavní podporu pro skutečné používání multithreadingu poskytuje standardní knihovna C++11.

Je poskytována třída vlákna ( std::thread), která akceptuje funkční objekt (a volitelný seznam argumentů, které mu mají být předány) ke spuštění v novém vláknu. Poskytnutím podpory pro sdružování vláken prostřednictvím členské funkce můžete vynutit zastavení vlákna před dokončením jiného spouštěcího vlákna std::thread::join(). Pokud je to možné, přístup k nativnímu ovladači vlákna je poskytován pro operace specifické pro platformu prostřednictvím členské funkce std::thread::native_handle().

Pro synchronizaci mezi vlákny jsou do knihovny přidány příslušné mutexy ( std::mutexatd std::recursive_mutex.) a proměnné podmínek ( std::condition_variablea ). std::condition_variable_anyJsou dostupné prostřednictvím zámků inicializace prostředků (RAII) ( std::lock_guarda std::unique_lock) a zamykacích algoritmů pro snadné použití.

Vysoce výkonná nízkoúrovňová práce někdy vyžaduje komunikaci mezi vlákny bez režie mutexů. To se provádí pomocí atomických operací na paměťových místech. Mohou volitelně specifikovat minimální limity viditelnosti paměti požadované pro operaci. K tomuto účelu lze také použít explicitní paměťové bariéry.

Knihovna vláken C++11 také obsahuje futures a přísliby pro předávání asynchronních výsledků mezi vlákny a třídu std::packaged_taskpro zabalení volání funkce, která může generovat takový asynchronní výsledek. Návrh futures byl kritizován, protože postrádá způsob, jak kombinovat futures a zkontrolovat splnění jediného slibu v sadě slibů.

Do budoucí bílé knihy C++ byly přidány další zařízení pro vytváření vláken na vysoké úrovni, jako jsou fondy vláken. Nejsou součástí C++11, ale očekává se, že jejich případná implementace bude postavena výhradně na funkcích knihovny vláken.

Nová funkce std::asyncposkytuje pohodlný způsob, jak spouštět úlohy a vázat výsledek jejich provádění k objektu souboru std::future. Uživatel si může vybrat, zda bude úloha probíhat asynchronně v samostatném vláknu, nebo synchronně v aktuálním vláknu čekajícím na hodnotu.

Hashovací tabulky

std::hash_seta std::hash_mapjsou již dlouho nestandardním rozšířením STL, ve skutečnosti implementovaným ve většině kompilátorů. V C++11 se staly standardními pod názvy std::unordered_seta std::unordered_map. Ačkoli se ve skutečnosti jedná o hashovací tabulky a standard neponechává mnoho prostoru pro pohyb, názvy jsou uvedeny ve stylu C++: nikoli „jak jsou implementovány“, ale „jaké jsou“.

Regulární výrazy

Nová knihovna, deklarovaná v záhlaví souboru <regex>, obsahuje několik nových tříd:

  • Regulární výrazy jsou reprezentovány jako instance std::regex;
  • výsledky vyhledávání jsou reprezentovány jako instance šablony std::match_results.

Funkce std::regex_searchse používá pro vyhledávání, pro operaci 'najít a nahradit' se používá funkce std::regex_replace. Funkce vrátí řetězec po provedení nahrazení. Algoritmy std::regex_searcha std::regex_replaceberou jako vstup regulární výraz a řetězec a vrací nalezené výsledky jako instanci std::match_results.

Příklad použití std::match_results:

const char * reg_esp = "[ ,. \\ t \\ n;:]" ; // Seznam oddělovacích znaků. // totéž lze provést pomocí "raw" řetězců: // const char *reg_esp = R"([ ,.\t\n;:])"; std :: regulární výraz rgx ( reg_esp ); // 'regex' je instancí třídy šablony // 'basic_regex' s parametrem šablony 'char'. std :: cmatch match ; // 'cmatch' je instancí třídy šablony // 'match_results' s parametrem šablony 'const char *'. const char * target = "Unseen University - Ankh-Morpork" ; // Opravuje všechna slova řetězce 'target' oddělená znaky z 'reg_esp'. if ( std :: regex_search ( target , match , rgx ) ) { // Pokud jsou v řetězci přítomna slova oddělená danými znaky. const size_t n = shoda . velikost (); for ( size_t a = 0 ; a < n ; a ++ ) { std :: string str ( shoda [ a ]. první , shoda [ a ]. druhá ); std :: cout << str << " \n " ; } }

Všimněte si, že dvojitá zpětná lomítka jsou vyžadována, protože C++ používá zpětná lomítka k escapování znaků. Můžete použít "raw strings" - další inovace standardu C++11.

Knihovna <regex>nevyžaduje žádné úpravy stávajících hlavičkových souborů ani instalaci dalších jazykových rozšíření.


Rozšiřitelné třídy generování náhodných čísel

Standardní knihovna C umožňovala generování pseudonáhodných čísel pomocí rand. Jeho chování se však může lišit v závislosti na implementaci.

Tato funkcionalita je rozdělena na dvě části: generátor generátoru, který obsahuje aktuální stav generátoru náhodných čísel a vytváří pseudonáhodná čísla, a rozdělení, které určuje rozsah a matematické rozdělení výsledku. Kombinací těchto dvou objektů vzniká generátor náhodných čísel.

Generátorové motory:

Distribuce:

Příklad:

#include <náhodné> #include <funkční> std :: uniform_int_distribution < int > rozdělení ( 0 , 99 ); std :: motor mt19937 ; _ // Mersenne vortex MT19937 auto generator = std :: bind ( distribuce , motor ); int náhodný = generátor (); // Získá náhodné číslo mezi 0 a 99. int random2 = distribution ( engine ); // Získání náhodného čísla přímo pomocí enginu a distribuce.



Plánované prvky nejsou zahrnuty ve standardu

Moduly Obrovský objem hlavičkových souborů vedl ke kvadratickému prodloužení doby kompilace: zvýšilo se jak množství kódu, tak počet modulů v jedné kompilační jednotce. Moduly by měly poskytovat mechanismus podobný souborům Delphi DCU nebo souborům třídy Java .

Odstraněné nebo zastaralé funkce

Viz také

Poznámky

  1. Herb Sutter , Máme mezinárodní standard: C++0x je jednomyslně schváleno Archivováno 11. prosince 2018 na Wayback Machine
  2. Scott Meyers , Souhrn dostupnosti funkcí C++11 v gcc a MSVC Archivováno 26. října 2011 na Wayback Machine , 16. srpna 2011
  3. ISO , ISO/IEC 14882:2011 Archivováno 29. ledna 2013 na Wayback Machine
  4. Název C++0x definovaný ve finálním návrhu N3290 Archivováno 20. června 2010 na Wayback Machine
  5. Stroustrup, Bjorn  – C++0x – další standard ISO C++ Archivováno 11. května 2011 na Wayback Machine
  6. Dokumenty výboru pro standardy C++ . Získáno 24. února 2008. Archivováno z originálu 18. března 2010.
  7. Zdroj C++ Bjarne Stroustrup ( 2. ledna 2006 ) Stručný pohled na C++0x . (Angličtina)

Dokumenty C++ Standards Committee

  •   Doc. 1401: Jan Kristoffersen (21. října 2002)Atomové operace s vícevláknovými prostředími
  •   Doc. 1402: Doug Gregor (22. října 2002)Návrh na přidání obálky objektů polymorfní funkce do standardní knihovny
  •   Doc. 1403: Doug Gregor (8. listopadu 2002)Návrh na přidání typů n-tic do standardní knihovny
  •   Doc. 1424: John Maddock (3. března 2003)Návrh na přidání typových vlastností do standardní knihovny
  •   Doc. 1429: John Maddock (3. března 2003)Návrh na přidání regulárních výrazů do Standardní knihovny
  •   Doc. 1449: B. Stroustrup, G. Dos Reis, Mat Marcus, Walter E. Brown, Herb Sutter (7. dubna 2003)Návrh na přidání aliasů šablon do C++
  •   Doc. 1450: P. Dimov, B. Dawes, G. Colvin (27. března 2003)Návrh na přidání univerzálních inteligentních ukazatelů do technické zprávy knihovny (revize 1)
  •   Doc. 1452: Jens Maurer (10. dubna 2003)Návrh na přidání rozšiřitelného zařízení s náhodnými čísly do standardní knihovny (revize 2)
  •   Doc. 1453: D. Gregor, P. Dimov (9. dubna 2003)Návrh na přidání obálky odkazů do standardní knihovny (revize 1)
  •   Doc. 1454: Douglas Gregor, P. Dimov (9. dubna 2003)Jednotná metoda pro výpočet návratových typů funkčních objektů (revize 1)
  •   Doc. 1456: Matthew Austern (9. dubna 2003)Návrh na přidání hashovacích tabulek do standardní knihovny (revize 4)
  •   Doc. 1471: Daveed Vandevoorde (18. dubna 2003)Reflektivní metaprogramování v C++
  •   Doc. 1676: Bronek Kozicki (9. září 2004) Nečlenpřetížený operátor přiřazení kopie
  •   Doc. 1704: Douglas Gregor, Jaakko Järvi, Gary Powell (10. září 2004)Variadic Templates: Exploring the Design Space
  •   Doc. 1705: J. Järvi, B. Stroustrup, D. Gregor, J. Siek, G. Dos Reis (12. září 2004)Decltype (a auto)
  •   Doc. 1717: Francis Glassborow, Lois Goldthwaite (5. listopadu 2004)explicitní definice třídy a výchozí definice
  •   Doc. 1719: Herb Sutter, David E. Miller (21. října 2004)Silně typovaná výčty (revize 1)
  •   Doc. 1720: R. Klarer, J. Maddock, B. Dawes, H. Hinnant (20. října 2004)Návrh na přidání statických tvrzení do základního jazyka (revize 3)
  •   Doc. 1757: Daveed Vandevoorde (14. ledna 2005)Pravoúhlé držáky (revize 2)
  •   Doc. 1811: J. Stephen Adamczyk (29. dubna 2005)Přidání typu long long do C++ (Revision 3)
  •   Doc. 1815: Lawrence Crowl (2. května 2005)ISO C++ Strategický plán pro multithreading
  •   Doc. 1827: Chris Uzdavinis, Alisdair Meredith (29. srpna 2005)Syntaxe Explicitního přepisu pro C++
  •   Doc. 1834: Detlef Vollmann (24. června 2005)Prosba o rozumnou podporu paralelního zpracování v C++
  •   Doc. 1836: ISO/IEC DTR 19768 (24. června 2005)Návrh technické zprávy o rozšířeních knihoven C++
  •   Doc. 1886: Gabriel Dos Reis, Bjarne Stroustrup (20. října 2005)Specifikace konceptů C++
  •   Doc. 1891: Walter E. Brown (18. října 2005)Pokrok směrem k Opaque Typedefs pro C++0X
  •   Doc. 1898: Michel Michaud, Michael Wong (6. října 2004)Přeposílání a zdědění konstruktéři
  •   Doc. 1919: Bjarne Stroustrup, Gabriel Dos Reis (11. prosince 2005)Seznamy inicializátorů
  •   Doc. 1968: V Samko J Willcock, J Järvi, D Gregor, A Lumsdaine (26. února 2006)Lambda výrazy a uzávěry pro C++
  •   Doc. 1986: Herb Sutter, Francis Glassborow (6. dubna 2006)Delegování konstruktérů (revize 3)
  •   Doc. 2016: Hans Boehm, Nick Maclaren (21. dubna 2002)Měla by volatilita získat atomicitu a sémantiku viditelnosti vláken?
  •   Doc. 2142: ISO/IEC DTR 19768 (12. ledna 2007)Stav vývoje C++ (mezi setkáními Portland a Oxford 2007)
  •   Doc. 2228: ISO/IEC DTR 19768 (3. května 2007)Stav vývoje C++ (Schůzky v Oxfordu 2007)
  •   Doc. 2258: G. Dos Reis a B. Stroustrupaliasy šablon
  •   Doc. 2280: Lawrence Crowl (2. května 2007)Thread-Local Storage
  •   Doc. 2291: ISO/IEC DTR 19768 (25. června 2007)Stav vývoje C++ (Schůzky v Torontu 2007)
  •   Doc. 2336: ISO/IEC DTR 19768 (29. července 2007)Stav vývoje C++ (Schůzky v Torontu 2007)
  •   Doc. 2389: ISO/IEC DTR 19768 (7. srpna 2007)Stav vývoje C++ (schůzky před Kona 2007)
  •   Doc. 2431: SC22/WG21/N2431 = J16/07-0301 (2. října 2007),Název pro nulový ukazatel: nullptr
  •   Doc. 2432: ISO/IEC DTR 19768 (23. října 2007)Stav vývoje C++ (po setkání Kona 2007)
  •   Doc. 2437: Lois Goldthwaite (5. října 2007)Explicit Conversion Operators
  •   Doc. 2461: ISO/IEC DTR 19768 (22. října 2007)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 2507: ISO/IEC DTR 19768 (4. února 2008)Stav vývoje C++ (před jednáním Bellevue 2008)
  •   Doc. 2544: Alan Talbot, Lois Goldthwaite, Lawrence Crowl, Jens Maurer (29. února 2008)Neomezené odbory
  •   Doc. 2565: ISO/IEC DTR 19768 (7. března 2008)Stav vývoje C++ (schůzka po Bellevue 2008)
  •   Doc. 2597: ISO/IEC DTR 19768 (29. dubna 2008)Stav vývoje C++ (před zasedáním Antipolis 2008)
  •   Doc. 2606: ISO/IEC DTR 19768 (19. května 2008)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 2697: ISO/IEC DTR 19768 (15. června 2008)Zápis ze zasedání WG21 8.–15. června 2008
  •   Doc. 2798: ISO/IEC DTR 19768 (4. října 2008)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 2857: ISO/IEC DTR 19768 (23. března 2009)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 2869: ISO/IEC DTR 19768 (28. dubna 2009)Stav vývoje C++ (po zasedání v San Franciscu 2008)
  •   Doc. 3000: ISO/ISC DTR 19769 (9. listopadu 2009)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 3014: Stephen D. Clamage (4. listopadu 2009)PROGRAM, PL22.16 Schůze č. 53, zasedání WG21 č. 48, 8.-13. března 2010, Pittsburgh, PA
  •   Doc. 3082: Herb Sutter (13. března 2010)C++0x Plán jednání
  •   Doc. 3092: ISO/ISC DTR 19769 (26. března 2010)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 3126: ISO/ISC DTR 19769 (21. srpna 2010)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 3225: ISO/ISC DTR 19769 (27. listopadu 2010)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 3242: ISO/ISC DTR 19769 (28. února 2011)Pracovní návrh, Standard pro programovací jazyk C++
  •   Doc. 3291: ISO/ISC DTR 19769 (5. dubna 2011)Working Draft, Standard for Programming Language C++
  •   Doc. 3290: ISO/ISC DTR 19769 (5. dubna 2011)FDIS, standard pro programovací jazyk C++
  •   Doc. 3337 : Datum: 2012-01-16 Pracovní návrh, Standard pro programovací jazyk C++

Odkazy

Literatura

  • Stanley B. Lippman, Josy Lajoye, Barbara E. Moo. C++ programovací jazyk. Základní kurz 5. vydání = C++ Primer (5. vydání). - M. : "Williams" , 2014. - 1120 s. - ISBN 978-5-8459-1839-0 .
  • Siddhártha Rao. Naučte se C++ za 21 dní, 7. vydání = Sams Naučte se C++ za jednu hodinu denně, 7. vydání. - M. : "Williams" , 2013. - 688 s. — ISBN 978-5-8459-1825-3 .
  • Štěpán Prata. Programovací jazyk C++ (C++11). Přednášky a cvičení, 6. vydání = C++ Primer Plus, 6. vydání (Knihovna pro vývojáře). - M .: "Williams" , 2012. - 1248 s. - ISBN 978-5-8459-1778-2 .