C++ 11 - nový ISO C++ standardní. Část 2

<< Obsah

Níže jsou uvedeny odpovědi na konkrétní otázky, jak indexovaných výše.


Co si myslíte o C + 11?

To je (pro mě) překvapivě časté otázky. To může být nejčastější otázka. Překvapivě, C + 11 se cítí jako nový jazyk: Kusy jen zapadají lépe než oni, a najdu si vyšší úroveň styl programování přirozenější než dříve a tak efektivní jako nikdy předtím. Pokud se nesměle přiblížit C + + jen jako lepší C nebo jako objektově-orientovaný jazyk, budete postrádat smysl. Tyto abstrakce jsou prostě pružnější a dostupnější než dříve.Spolehněte se na starou mantru: Pokud si myslíte, že na to, jak zvláštní nápad nebo objekt, zastupuje ji přímo v programu, na model reálného světa objektů, a abstrakce přímo v kódu. Je to teď snazší: Vaše nápady bude mapovat vyčíslení , objekty, třídy (např. kontrola výchozích ), hierarchie tříd (např. vrozené konstruktéři ), šablony, aliasy , výjimky, smyčky , závity , atd., spíše než na jeden "jedna velikost fits all "abstrakce mechanismus.

Můj ideální je použít zařízení programovacího jazyka pomoci programátorům přemýšlet o návrhu systému a implementace. Myslím, že C + 11 může udělat - a to nejen pro C + + programátory, ale pro programátory používaných k mnoha moderních programovacích jazyků v obecném a velmi široký oblasti systémů programování.

Jinými slovy, já jsem stále optimista.

 


Kdy bude C + 11 je formální standard?

Nyní je! První návrh pro formální připomínky byl vyroben v září 2008. Final International Návrh normy (FCD) jednomyslně schválena ISO C + + výbor 25. března 2011. To bylo formálně schválen 21-0 národního hlasu v srpnu 2011. Standard byl publikován v letošním roce (2011).

Následující výčet je nový standard nazvaný C + 11 (protože byla publikována v roce 2011) Osobně bych dal přednost holý C + + a použít rok marker pouze tehdy, když musím rozlišit to od předchozích verzí C + +, jako je ARM C + +, C + 98 a C + 03. Pro přechodné období, i nadále používat C + +0 x v místech. Myslete na "x" jako hexadecimální.

 


Kdy bude překladače provádět C + 11?

V současné době doručení překladače (např. GCC C + +, zvonění C + +, IBM C + +, a Microsoft C + +), které již realizovat mnoho C + 11 funkce. Například, je zřejmé, a populární loď všech nebo většiny nových standardních knihoven.

Očekávám, že další a další funkce, aby se stal k dispozici s každou novou verzí. Očekávám, že první kompletní C + 11 kompilátor někdy v roce 2012, ale je mi jedno, hádat, když taková překladač dodání nebo když každý kompilátor bude poskytovat všechny C + 11. Beru na vědomí, že každý C + 11 funkce již byla provedena někým někde, takže tam je implementace zkušenosti k dispozici pro realizátory spoléhat se na.

Zde jsou odkazy na C + 11 informace z dodavatelé:

 


Kdy budou nové standardní knihovny k dispozici?

Počáteční verze nových standardních knihoven jsou v současné době dodávají s GCC, Clang a Microsoft implementace, a jsou k dispozici od boost .

 


Co nové jazykové vlastnosti se C + 11 nabízí?

Nemusíte zlepšit jazyk pouhým přidáním všechny funkce, které někdo považuje za dobrý nápad. Ve skutečnosti, v podstatě každý rys většiny moderních jazyků bylo navrženo ke mně C + + někým: Zkuste si představit, co nadmnožinou C99, C #, Java, by Haskell, Lisp, Python, a Ada vypadat. Chcete-li tento problém složitější, nezapomeňte, že to není možné odstranit starší funkcí, a to iv případě, že výbor souhlasí s tím, že jsou špatné: zkušenost ukazuje, že uživatelé nutí každý realizátor, aby poskytování zastaralé a zakázal funkce podle kompatibility přepínačů (nebo standardně) po celá desetiletí.

Chcete-li pokusí se vybrat racionálně od záplavy podnětů jsme navrhli sadu specifických koncepčních cílů . Nemohli jsme úplně sledovat a oni nebyli dostatečně dokončit vést výbor v každém detailu (a IMO by nebylo možné být tak kompletní).

Výsledkem byl jazyk s velmi zlepšila odběru mechanismů. Rozsah abstrakcí, které C + + může vyjadřovat elegantně, pružně, a na nulové náklady ve srovnání s ručně řemeslně-specializovaného kódu výrazně zvýšil. Když říkáme "abstrakce" lidé často si myslím, že "třídy" nebo "objekty." C + 11 jde mnohem dál: Rozsah uživatelem definovaných typů, které mohou být čistě a bezpečně vyjádřené rostla s přidáním funkcí, jako inicializátor-seznamy , jednotný inicializace , šablony přezdívky , rvalue reference , selhání a mazat funkcí , a variadic šablony . Jejich realizace snížila s funkcemi, jako je například auto , zděděných stavitelů , a decltype . Tato vylepšení jsou dostatečné, aby se C + 11 Připadám si jako nového jazyka.

Pro seznam přijatých jazykových funkcí, viz seznam funkcí

 


Jaké nové standardní knihovny se C + 11 nabízí?

Já bych rád viděl více standardních knihoven. Uvědomte si však, že standardní knihovna definice je již asi 70% z normativního textu normy (a to se nepočítá C standardní knihovna, která je součástí odkazem). I když někteří z nás by rád viděl mnohem více standardních knihoven, nikdo by tvrdit, že knihovna pracovní skupina byla líná. Je také třeba poznamenat, že C + 98 knihovny byly výrazně zlepšila díky použití nových jazykových prvků, jako jsou inicializátor-seznamy , rvalue odkazy , variadic šablon , noexcept , a constexpr . C + 11 standardní knihovna je snadnější k použití a poskytuje lepší výkon, že C + 98 jeden.

Pro seznam přijatých knihoven, viz knihovny seznam komponent .

 


Jaké byly cíle C + 11 úsilí?

C + + je univerzální programovací jazyk s zaujatostí vůči systémů programů, které

  • je lepší C
  • podporuje datové abstrakce
  • podporuje objektově orientované programování
  • podporuje obecný programování

Celkové cíle C + 11 úsilí bylo posílit, že:

  • Udělejte C + + lepší jazyk pro systémy programování a budova knihovny - to je, stavět přímo na C + + 's příspěvky na programování, spíše než poskytování specializovaných zařízení pro určité sub-komunity (např. numerická výpočtu nebo Windows-style vývoj aplikací).
  • Udělejte C + + jednodušší učit a učit se - díky zvýšené jednotnosti, silnější záruky, a zařízení podporujících noviců (tam bude vždy být více než nováčci odborníků).

Samozřejmě, toto je děláno za velmi striktních omezení kompatibility. Jen velmi zřídka je výbor ochoten porušit standardy odpovídajících kód, i když to se stalo, když nové klíčové slovo (např. static_assert ,nullptr , a constexpr je zaveden).

Pro více informací viz:

 


Jaké konkrétní provedení cílům řídí výbor?

Samozřejmě, různí lidé a různé organizace zabývající se standardizací mají poněkud odlišné cíle, a to zejména pokud jde o detaily a priorit. Také, detailní cíle v průběhu času mění. Mějte prosím na paměti, že výbor nemůže ani dělat vše, že každý souhlasí s tím, by bylo dobré věci - skládá se z dobrovolníků s velmi omezenými zdroji. Nicméně, zde jsou souborem kritérií, které bylo dosaženo skutečného využití v diskusi o jaké funkce a knihovny byly vhodné pro C + 11:

  • Udržovat stabilitu a kompatibilitu - neporušují starý kód, a pokud jste naprosto nutné, neporušují tiše.
  • Raději knihovny rozšíření jazyka - ideální na které výbor nebylo všechno, že úspěšná, příliš mnoho lidí ve výboru a jinde dávají přednost "skutečné vlastnosti jazyka."
  • Preferuji všeobecnost na specializaci - zaměření na zlepšení abstrakce mechanismy (třídy, šablony, atd.).
  • Podpora odborné i nováčkům - nováčci mohou být pomáhal tím lepší knihoven a přes více obecných pravidel, odborníci potřebují obecné a efektivní funkce.
  • Zvýšení bezpečnost typů - především když zařízení, které umožňují programátorům, aby se zabránilo typu nebezpečných vlastností.
  • Zlepšení výkonu a schopnost pracovat přímo s hardwarem - aby C + + ještě lepší pro embedded systémy programování a vysoce výkonné výpočty.
  • Fit do reálného světa - zvážit nástroje řetězy, implementační náklady, problémy přechodu, ABI problémy, vyučování a učení, atd.

Všimněte si, že začlenění prvků (nový a starý) pro práci v kombinaci je klíčem - a většinu práce. Celek je mnohem více než pouhý součet jeho částí.

Další způsob, jak se dívat na podrobných cílů je podívat se na oblasti použití a stylů použití:

  • Model stroje a souběžnost - silnější zárukou pro lepší a zařízení pro použití moderní hardware (např. multicores a slabě soudržné paměť modely). Příkladem jsou nitě ABI , futures , závit-místní úložiště , aatomové ABI .
  • Generic programování - GP patří mezi velkých úspěchů C + 98, jsme potřebovali zlepšit podporu pro něj na základě zkušeností. Příkladem jsou auto a šablony aliasy .
  • Systémy programování - zlepšit podporu úzké-to-the-hardware programování (např. low-level programování embedded systémů) a účinnost. Příkladem jsou constexpr , std :: array , a zobecněné POD .
  • Budova knihovny - odstranění omezení, neefektivnost, a nepravidelnosti z odběru mechanismů. Příkladem jsou inline namespace , zděděné konstruktéři , a rvalue odkazy .

 


Kde mohu najít výboru papíry?

Přejít na papíry části internetových stránkách Výboru . Tam budete s největší pravděpodobností utopil v detailech. Podívejte se na "vydává seznamy" a "stav" (např. státní evoluce (červenec 2008) ) seznamů.Klíčovými skupinami jsou

  • Core (CWG) - zabývající se jazyk technických problémů a formulace
  • Evolution (EWG) - zabývající se návrhy jazykových funkcí a problémů překračujících jazyk / knihovna hranici
  • Library (LWG) - zabývající se návrhy knihovny zařízení

Zde je nejnovější návrh C + 11 standardu .

 


Kde najdu akademické a technické papíry o C + 11?

Tento seznam je pravděpodobně neúplný - a pravděpodobně často chodit ven data, jak lidé píší nové dokumenty. Pokud narazíte na papír, který by měl být tady a není, zašlete jej. Také, ne všechny dokumenty zcela up-to-data s nejnovějšími vylepšeními normy. Budu se snažit, aby připomínky proudu.

 


Kde jinde si mohu přečíst o C + 11?

Množství informací o C + 11 roste jako standardní blíží dokončení a C + + implementace začít poskytovat nové vlastnosti jazyka a knihoven. Zde je krátký seznam zdrojů:

 


Existují nějaké videa o C + 11?

(Pro lidi, kteří mě znají to je důkaz, že to opravdu je FAQ, spíše než série mých vlastních oblíbených otázek, já nejsem fanoušek videa na technických témat - najdu video působí rušivě a slovní formát příliš pravděpodobné, obsahovat drobné technické chyby).

Ano:

 


Je C + 11 těžké se naučit?

No, protože nemůžeme odebrat všechny významné vlastnosti z C + + bez lámání velké množství kódu, bude C + 11 větší, že C + 98, takže pokud chcete vědět každé pravidlo, učení C + 11 bude těžší . To nás staví jen dva nástroje pro zjednodušení (z pohledu studentů):

Je zřejmé, že "zdola nahoru" vyučování / učení styl zrušit takové výhody, a je v současné době (samozřejmě) jen velmi málo materiálu, který má jiný přístup. To by mělo měnit s časem.

 


Jak se výbor fungovat?

ISO normy výbor, SC22 WG21, funguje podle pravidel ISO pro tyto výbory. Kupodivu, tato pravidla nejsou standardizovány a v průběhu času měnit.

Mnoho zemí má národní normalizační orgány s aktivními C + + skupin. Tyto skupiny pořádat setkání, koordinovat přes web, a některé vyslaly své zástupce na zasedání ISO. Kanada, Francie, Německo, Švýcarsko, Velká Británie, USA a jsou přítomny ve většině schůzí. Dánsko, Nizozemsko, Japonsko, Norsko, Španělsko, a jiní jsou zastoupeny v osobě méně často.

Hodně práce jde o in-mezi schůzkami přes web a výsledky jsou zaznamenány jako číslovaných papíry výboru na WG21 stránkách.

Výbor se schází dvakrát až třikrát ročně za týden pokaždé. Nejvíce práce na těchto setkáních jsou v sub-pracovních skupin, jako jsou "základní", "knihovna", "Evolution", a "souběžnosti." Podle potřeby, tam jsou také v době mezi zasedáními ad hoc pracovních skupin na konkrétní naléhavých témat, jako je například "pojmy" a "paměťový model." Hlasování probíhá na hlavních setkáních. Za prvé, pracovní skupiny držet "slaměné hlasů", aby se zjistilo, zda problém je připraven pro prezentaci výboru jako celku. Pak se výbor jako celek hlasování (jeden člen jeden hlas) a pokud něco přijal národy hlas. Dbáme, abychom se nedostali do situace, kdy většina přítomných a národy nesouhlasí - řízení, pokud to je případ by zaručují dlouhodobou diskusi. Konečné hlasování o oficiálních návrhů se provádí poštou podle národních standardizačních orgánů.

Výbor má formální spojení s C norem skupiny (SC22 WG14) a POSIX, a více či méně formální vztahy s několika dalšími skupinami.

 


Kdo je na výboru?

Výbor se skládá z velkého počtu lidí (asi 200) z nichž je asi 60 obrací se na týdenních setkáních dvakrát nebo třikrát ročně. Kromě toho existují národní normalizační skupiny a setkání v několika zemích. Většina členů přispívat buď účastí na schůzích, tím, že se účastní e-mailových diskusí, nebo podají papíry pro Projednávání. Většina členů má přátele a kolegy, kteří jim pomáhají. Od prvního dne # 1, výbor má členy z mnoha zemí a při každém setkání lidí z půl tuctu do tuctu zemí zúčastnit. Konečné hlasování se provádí asi 20 národních normalizačních orgánů. Tak, ISO C + + je standardizace poměrně masivní úsilí, není malá ucelená skupina lidí, kteří pracují pro vytvoření dokonalého jazyka pro "lidi, stejně jako sebe." Standardní je to, co tato skupina dobrovolníků se mohou dohodnout na jako to nejlepší, co mohou produkovat, abyvšichni mohli žít.

Přirozeně, mnoho (ale ne všechny) z těchto dobrovolníků se denně práci zaměřené na C + +: Máme kompilátor spisovatele, nástroje stavitelé, knihovny spisovatele, aplikace stavitele (příliš málo z těch,), výzkumníci (jen pár), poradenství, test-apartmá stavitelé a další.

Zde je velmi zkrácený seznam zapojených organizací: Adobe, Apple, Boost, Bloomberg, EDG, Google, HP, IBM, Intel, Microsoft, Red Hat, ne.

Zde je krátký seznam jmen členů, které můžete se setkali v literatuře nebo na internetu: Dave Abrahams , Matt Austern , Pete Becker , Hans Boehm , Steve Clamage , Lawrence Crowl , Beman Dawes , Francis Glassborow , Doug Gregor , Pablo Halpern , Howard Hinnant , Jaakko Jarvi , John Lakos , Alisdair Meredith, Jens Maurer, Jason Merrill, Sean Parent , PJ Plauger , Tom Plum , Gabriel Dos Reis , Bjarne Stroustrup , Herb Sutter , David Vandevoorde , Michael Wong . Omlouvám se za 200 + současné a minulé členy, že jsem nemohl seznamu. Také, prosím, na vědomí, se autorka seznamy na různých dokumentech: standard napsal (mnoha) jednotlivcům, nikoliv anonymní výboru.

Můžete získat lepší představu o dechu a hloubku odborných znalostí podílí tím, že zkoumá autor seznamy na WG21 papíry , ale pamatujte, že jsou hlavní přispěvatelé do norem úsilí, kteří nemají napsat hodně.

 


Bude existovat C + 1 y?

Téměř jistě - a to nejen proto, že výbor sklouzl lhůtu pro C + +0 x. Doufá, že / plánuje jsem nejčastěji slyšet vyjádřil, že výbor by měl začít na C + 1 y ihned po C + 11 hlasů a usilovat o zkratováním období před C + 1 y. Desetiletí mezi standardy je příliš dlouhá pro současného tempa technologie, takže někteří lidé sní o tříletého období mezi revizemi. Já osobně si myslím, že 5 let je realističtější, C + 16 někdo?

 


Co se stalo s "koncepcí"?

"Pojmy" byl rys navržen tak, aby přesně specifikaci požadavků na šablony argumenty. Bohužel, výbor rozhodl, že další práce na koncepci by mohla vážně zpozdit standard a hlasoval, aby odstranit funkci z pracovního dokumentu, viz můj dopis C + +0 x "Smazat pojmy" rozhodnutí a rozhovor DevX na pojmy a důsledky pro C + +0 x vysvětlení.

Vzhledem k tomu, že je pravděpodobné, že "pojmy" v nějaké formě nebo jiný bude součástí novější verzi C + +, jsem se zrušuje pojem oddíly z tohoto dokumentu, ale opustil je na konci:

 


Existují nějaké funkce se vám nelíbí?

Ano. Tam jsou také funkce v C + 98, které se mi nelíbí, jako makra. Otázkou není, zda se mi líbí něco, nebo jestli jsem najít to užitečné pro něco, co chci dělat. Otázkou je, zda má někdo pocit, dost o potřebě přesvědčit ostatní, aby podpořily myšlenku, případně pokud některé použití je tak hluboce zakořeněný v uživatelské komunity, že potřebuje podporu.

 


__cplusplus

V C + 11 budou cplusplus makro __ být nastavena na hodnotu, která se liší od (je větší než) aktuální 199711L.

 


auto - odpočet typu z inicializátor

Zvážit

 
	  auto x = 7; 
 

Zde x bude mít typ int, protože to je typ jeho inicializátor. Obecně platí, že můžeme napsat

 
	  auto x = výraz; 
 

a typ X bude typ hodnoty vypočtené z "výraz".

Použití auto odvodit typ proměnné od jejího inicializátor je zřejmě nejužitečnější, když tento typ je buď těžké přesně vědět, nebo těžké psát. Zvažte:

 template <class T> void printall (const vector <T> & v) {for (auto p = v.begin ();! p = v.end (); + + p) cout << * p << "\\\\n ";} 

V C + 98, museli bychom napsat

 
	  template <class T> void printall (const vector <T> & v) 
	  { 
		  pro (TypeName vector <T> :: const_iterator p = v.begin (); p = v.end ();! + + p) 
			  cout << * p << "\\\\n"; 
	  } 
 

Pokud typ proměnné závisí kriticky na šablony argumentu, že může být opravdu těžké psát kód bez auto. Například:

 
	  template <class T, třída U> void multiply (const vector <T> & vt, const vector <U> & vu) 
	  { 
		  // ... 
		  auto tmp = vt [i] * vu [i]; 
		  // ... 
	  } 
 

Typ tmp by mělo být to, co dostanete z vynásobení T byla provedena U, ale přesně to, co to je může být těžké pro člověka čtenář zjistit, ale samozřejmě kompilátor ví, že poté, co přišel na to, jaké konkrétní T aU je se zabývá.

Auto prvek má rozdíl být nejdříve být navrženo a realizovány: Já jsem to pracovat v mém Cfront provádění na začátku 1984, ale byl nucen vzít to z důvodu problémů s kompatibilitou C. Tyto problémy s kompatibilitou zmizel, když C + 98 a C99 přijal odstranění "implicitní int", to znamená, oba jazyky vyžadují, aby každá proměnná a funkce musí být definována s výslovným typu. Starý Význam auto ("toto je lokální proměnná") je nyní nezákonné. Několik členů výboru trawled v milionech řádků kódu nálezu jen hrst používá - a většina z nich byla v testovacích sad nebo se zdála být chyby.

Být především zařízení pro zjednodušení značení v kódu, auto nemá vliv na standardní knihovny specifikaci.

Viz také

 


Rozsah-pro prohlášení

Rozsah prohlášení umožňuje iterovat "rozsah", což je něco, co můžete iterovat jako STL-sekvence definované begin () a end (). Všechny standardní nádoby mohou být použity jako rozsah, jako může std :: string, inicializátor seznam, pole, a vše, pro které se definuje begin () a end (), např. IStream. Například:

 
  void f (vector <double> & v) 
  { 
	  pro (auto x: v) cout << x << "\\\\n"; 
	  pro (auto & x: v) + + x; // pomocí odkazu, které nám umožní změnit hodnotu 
  } 
 

Můžete si přečíst, že "pro všechny x v v" prochází počínaje v.begin () a iterace až v.end (). Jiný příklad:

 
	  pro (const auto x: {1,2,3,5,8,13,21,34}) cout << x << "\\\\n"; 
 

Začít () (a konec ()) může být členem být nazýván x.begin () nebo volně stojící funkci, která se nazývá začít (x). Člen verze má přednost.

Viz také

 


P-lomené závorky

Zvážit

 
	  Seznam <vector <string>> LVS; 
 

V C + 98 je syntaktická chyba, protože není tam žádný prostor mezi dvěma> S. C + 11 uznává takové> jako korektní ukončení dvou seznamů šablony argumentů.

Proč tomu tak bylo někdy problém? Kompilátor front-end je organizována analyzuje / etapy. Jedná se o nejjednodušší modelu:

  • lexikální analýza (doplní žetony od postav)
  • syntaktická analýza (kontrole gramatiky)
  • kontrola typu (najít typ jmen a výrazů)

Tyto fáze jsou v teorii a někdy v praxi přísně odděleny, takže lexikální analyzátor, který zjistí, že >> je token (obvykle znamená pravý-Shift nebo vstup) nemá představu o jeho významu, zejména, že nemá představu o tom, šablon nebo vnořené seznamy šablony argumentů. Nicméně, aby se, že příklad "správné" tři etapy nějak spolupracovat. Klíčovým postřehem, který vedl k problému se řeší to, že každý C + + kompilátor již se pochopit problém, takže by to mohlo dát slušné chybové zprávy.

Viz také

 


kontrolu nad výchozí: výchozí Nastaveni odstranění

Společný úsloví "zakazující kopírování" muze byt nyní vyjádřena Primo:

 
	  class X { 
		  // ... 

		  X & operator = (const X &) = smazat; // Zakázat kopírování 
		  X (const X &) = smazat; 
	  }; 

Naopak, můžeme vzít výslovně tvrdí, ze chceme-li výchozí Kopie chování:

 
	  třída Y { 
		  // ... 
		  Y & operator = (const Y &) = výchozí, // výchozí Kopie sémantika 
		  Y (const Y &) = výchozí; 
	  } 
  }; 

Byt explicitní o výchozím Nastaveni JE nadbytečný.   Nicméně zmínky o operacích kopírování (horší) uživatel explicitně definuje kopírování Operace chtěl DAT výchozí chování nejsou neobvyklé.   Ponechává na kompilátoru k Provedení výchozí chování JE jednodušší, mene náchylné k chybám, Casto VEDE k lepšímu objektového kódu.

"Default" mechanismus muze byt používán pro Jiný Ucel, ktery MA výchozí. "Delete" mechanismus může být používán pro jiný účel. Například, můžeme odstranit nežádoucí konverzi, jako je tento:

 
	  struct {Z 
		  // ... 

		  Z (long long); // může inicializovat dlouho, dlouho 
		  Z (dlouhý) = smazat; //, ale ne něco méně 
	  }; 

Viz vzít


ovládání výchozí: Přesouvání a kopírování

Ve výchozím nastavení, třída má 5 operací:

  • Kopie zadání
  • kopírovací konstruktor
  • přesunout přiřazení
  • pohybovat konstruktor
  • ničitel

Pokud deklarujete některý z těch, musíte vzít v úvahu všechny a explicitně definovat nebo prodlení ty, které chcete. Myslete na kopírování, přesouvání, a ničení jako úzce související činnosti, spíše než jednotlivých operací, které můžete libovolně kombinovat a zápas - můžete zadat libovolné kombinace, ale jen málo kombinací smysl sémanticky.

Pokud se kterýkoli tah, kopírování, nebo destruktor explicitně uvedeno (prohlásil, definovaný, = default , nebo = delete) uživatelem, není pohyb generován automaticky. Pokud se kterýkoli tah, kopírování, nebo destruktor explicitně uvedeno (prohlásil, definovaný, = default, nebo = delete) uživatelem, veškeré nehlášení operace kopírování jsou generovány ve výchozím nastavení, ale to se již nepoužívá, takže se nemusíte spoléhat na to. Například :

 
	  class X1 { 
		  X1 & operator = (const X1 &) = smazat; // Zakázat kopírování 
	  }; 

To implicitně také zakáže přemísťování X1 s. Copy inicializace je povoleno, ale zavržené.

 
	  třída X2 { 
		  X2 & operator = (const X2 &) = výchozí; 
	  }; 

To implicitně také zakáže přemísťování X2 s. Copy inicializace je povoleno, ale zavržené.

 
	  class X3 { 
		  X3 & operator = (X3 &&) = smazat; // Zakázat stěhování 
	  } 

To implicitně také zakáže kopírování X3 s.

 
	  class X4 { 
		  ~ X4 () = smazat; // Zakázat zničení 
	  } 

To implicitně také zakáže přemísťování X4 s. Kopírování je povoleno, ale zavržené.

Důrazně doporučujeme, pokud jste prohlásil jeden z těchto pěti funkcí, můžete explicitně deklarovat všechny. Například:

 
	  šablona <class T> 
	  class Handle { 
		  T * p; 
	  public: 
		  Rukojeť (T * pp): p {pp} {} 
		  ~ Handle () {delete p;} // user-definované destructor: žádná implicitní kopírování a přesun  

		  Rukojeť (Handle && h): p {hp} {hp = nullptr;}; // převod vlastnictví 
		  Rukojeť & operator = (držadlo && h) {delete p, p = hp; hp = nullptr; return * this;} // převod vlastnictví 

		  Rukojeť (const Handle &) = smazat; // no copy 
		  Rukojeť & operator = (const Handle &) = smazat; 

		  // ... 
	  }; 

Viz vzít


enum class - rozsahem a důrazně zadali výčtové typy

Enum class es ("nové výčtové typy", "silné výčty") adresa tři problémy s tradičními C + + výčtů:

  • konvenční enum s implicitně převést na int, způsobuje chyby, když někdo nechce výčet působit jako celé číslo.
  • konvenční enum s exportovat číselníky na okolní rozsahu, což způsobuje název střety.
  • základní typ výčtového typu nelze určit, způsobuje zmatenost, problémy s kompatibilitou, a dělá dopředu prohlášení nemožné.

enum class s ("silné výčtové typy") jsou silně zadali a rozsahem:

 
	  enum {Alert zelená, žlutá, volby, red}; // tradiční enum 

	  enum class Color {red, blue}; // rozsahem a důrazně zadali enum 
	                                    // No export enumerátor jmen do ohradní působnosti 
	                                    // No implicitní převod na int 
	  enum class trafficlight {červená, žlutá, zelená}; 

	  Upozorní = 7; // chyba (jako vždy v C + +) 
	  Barva c = 7; // error: no int-> Konverze barev 

	  int a2 = red; // ok: Alert-> int konverze 
	  int a3 = Alert :: red; // error v C + 98, ok v C + 11 
	  int a4 = blue; // error: modrá není v rozsahu 
	  int a5 = Barva :: modrá; // error: ne Barva-> int konverze 

	  Barva a6 = Barva :: modrá; // ok 

Jak je uvedeno, může tradiční enum s prací jako obvykle, ale nyní volitelně nárok s názvem výčtu.

Nové výčtové typy jsou "enum class", protože kombinují aspekty tradičních výčtů (názvy hodnoty) s aspekty tříd (rozsahem členů a absense konverzí).

Být schopen určit základní typ umožňují jednodušší interoperabilita a zaručené velikosti výčtů:

 
	  enum class Barva: char {red, blue}; // compact charakter 

	  enum class trafficlight {červená, žlutá, zelená}; // výchozí, základní typ int 

	  enum E {E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U}; // jak velký je E? 
	                                                   // (Bez ohledu na stará pravidla říkají; 
	                                                   // Tj. "implementace definované") 

	  enum EE: unsigned long {EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U}; // nyní můžeme být konkrétní 

To také umožňuje vpřed prohlášení o enum s:

 
	  enum class Color_code: char; // (dopředu) prohlášení 
	  void foobar (Color_code * p) // použití dopředu prohlášení 
	  // ... 
	  enum class Color_code: char {červená, žlutá, zelená, modrá}; // definice 

Základní typ musí být jeden z podepsaných nebo nepodepsané celočíselné typy, výchozí hodnota je int.

V standardní knihovny, se používají výčtu třídy

  • Pro mapování systémy specifické chybové kódy: V : Enum class ERRC;
  • Pro ukazatele ukazatel bezpečnosti: V : Enum class pointer_safety {uvolněná, přednostní, přísná};
  • Pro I / stream chyb O: v : Enum class io_errc {proud = 1};
  • Pro asynchronní komunikace chyb: V : Enum class future_errc {broken_promise, future_already_retrieved, promise_already_satisfied};

Některé z nich mají operátory, jako například == definované.

Viz vzít


constexpr - generalizovaná a zaručené konstantní výrazy

Constexpr mechanismus

  • poskytuje obecnější konstantní výrazy
  • umožňuje konstantní výrazy zahrnující uživatelem definované typy
  • poskytuje způsob, jak zaručit, že inicializace se provádí v době kompilace

Zvážit

 
	  enum {Flags dobrá = 0, nevyhověl = 1, špatný = 2, eof = 4}; 

	  constexpr int Provozovatel | (Flags f1, f2 Vlajky) {return Vlajky (int (f1) | int (f2));} 

	  void f (x) Vlajky 
	  { 
		  switch (x) { 
		  Případ špatné: / * ...  * / Break; 
		  Případ eof: / * ...  * / Break; 
		  Případ špatné | eof: / * ...  * / Break; 
		  default: / * ...  * / Break; 
		  } 
	  } 

Zde constexpr říká, že funkce musí být jednoduchého formuláře tak, že to může být hodnoceno v době kompilace, pokud daná konstanta výrazy argumenty.

Kromě být schopen vyhodnotit výrazy v době kompilace, chceme být schopni požadovat výrazy mají být hodnoceny v době kompilace; constexpr před variabilní definice to dělá (a implikuje const):

 
	  constexpr int x1 = špatná | eof; // ok 

	  void f (Flags f3) 
	  { 
		  constexpr int x2 = špatná | f3; // chyba: nelze hodnotit v době kompilace 
		  int x3 = špatná | f3; // ok 
	  } 

Typicky chceme v době kompilace hodnocení záruku na globální nebo jmenný prostor objektů, často pro objekty chceme umístit v Read-Only skladování.

Toto funguje také pro objekty, pro které konstruktéři jsou jednoduché dost být constexpr a výrazy zahrnující takové objekty:

 
	  struct Point { 
		  int x, y; 
		  constexpr Point (int xx, yy int): x (xx), y (rr) {} 
	  }; 

	  constexpr Point origo (0,0); 
	  constexpr int z = origo.x; 

	  constexpr Point [] = {bod (0,0), bod (1,1), bod (2,2)}; 
	  constexpr int x = [1] ​​x;. // x se 1 

Vezměte prosím na vědomí, že constexpr není univerzální náhrada za konst (nebo naopak):

  • const 's primární funkcí je vyjádřit myšlenku, že objekt není upravený přes rozhraní (i když může být objekt velmi dobře možno změnit prostřednictvím jiných rozhraní). To jen tak se stane, že prohlášení objekt const poskytuje vynikající možnosti optimalizace pro kompilátor. Zejména, pokud je objekt prohlášen const a její adresa není přijato, kompilátor je často schopen zhodnotit jeho inicializátor v době kompilace (i když to není zaručeno) a udržet tento objekt ve svých tabulkách, neemitují jej do generovaného kódu .
  • constexpr 's primární funkcí je rozšířit rozsah toho, co může být počítán v době kompilace, takže například výpočet typ bezpečné. Objekty prohlásil constexpr mají inicializátor hodnoceny v době kompilace, ale jsou v podstatě hodnoty uchovávané v kompilátoru tabulek a jen vypouštěné do generovaného kódu v případě potřeby.

Viz vzít


decltype - typ výrazu

decltype (E) je typ ("prohlásila typ") názvu nebo výrazu E a mohou být použity v prohlášení Například.:

 
	  void f (const vector <int> &, vector <float> & b) 
	  { 
		  typedef decltype ([0] * b [0]) Tmp; 
		  for (int i = 0; i <b.size (); + + i) { 
			  Tmp * p = new Tmp ([i] * b [i]); 
			  // ... 
		  } 
		  // ... 
	  } 

Tento pojem byl populární v generického programování pod značkou "typeof" na dlouhou dobu, ale typeof implementace při skutečném použití, jsou neúplné a neslučitelné, takže standardní verze se jmenuje decltype.

Pokud potřebujete pouze typ pro proměnné, které jste o inicializaci auto je často jednodušší výběr. Vy opravdu potřebujete decltype pokud potřebujete typ pro něco, co není proměnná, například návratový typ .

Viz vzít

  • C + + Návrh 7.1.6.2 Jednoduchý typ specifikátory
  • [Str02] Bjarne Stroustrup. Předloha návrhu na "typeof". C + + reflektor zpráva c + + std-ext-5364, říjen 2002. (Původní návrh).
  • [N1478 = 03-0061] Jaakko Jarvi, Bjarne Stroustrup, Douglas Gregor, a Jeremy Siek: Decltype a auto (původní návrh).
  • [N2343 = 07-0203] Jaakko Jarvi, Bjarne Stroustrup, a Gabriel Dos Reis: Decltype (revize 7): Navržené znění .

Inicializátor seznamy

Zvážit

 
	  vektor <double> v = {1, 2, 3.456, 99,99}; 
	  seznam <pair <string,string>> Jazykové = { 
		  {"Nygaard", "Simula"}, {"Richards", "BCPL"}, {"Ritchie", "C"} 
	  };  
	  mapa <vector <string>, vektorová <int>> let = { 
		  {{"Maurice", "Vincent", "Wilkes"}, {1913, 1945, 1951, 1967, 2000}}, 
		  {{"Martin", "Ritchards"}, {1982, 2003, 2007}},  
		  {{"David", "John", "Wheeler"}, {1927, 1947, 1951, 2004}} 
	  };  

Inicializátor seznamy nejsou jen pro pole nic víc. Mechanismus pro přijetí na {}-seznam je funkce (často konstruktoru) přijímá argument typu std :: initializer_list <T> Například.:

 
	  void f (initializer_list <int>); 
	  f ({1,2}); 
	  f ({23,345,4567,56789}); 
	  f ({}); // prázdný seznam 
	  f {1,2}; // error: volání funkce () chybí 

	  years.insert ({{"Bjarne", "Stroustrup"}, {1950, 1975, 1985}}); 
 

Initializer seznam může být libovolné délky, ale musí být homogenní (všechny prvky musí být z typu šablony argumentu, T, nebo se dá rozložit na T).

Kontejner může provádět initializer-seznam konstruktor takto:

 
	  template <class E> class vector { 
	  public: 
   		  vector (std :: initializer_list <E> s) // inicializace-list konstruktor 
   		  { 
      			  rezervy (s.size ()); // dostat správné množství prostoru 
      			  uninitialized_copy (s.begin (), s.end (), elem); // inicializace prvků (v elem [0: s.size ())) 
			  sz = s.size (); // set vector velikosti 
   		  } 
   	
		  // ...  jako dříve ... 
	  }; 

Rozdíl mezi přímým inicializace a kopírování inicializace je zachována {}-inicializaci, ale stává se relevantní méně často, protože {} inicializace. Například, std :: vectorexplicitní konstruktor z int a initializer-seznam konstruktoru:

 
	  vector <double> v1 (7); // ok: v1 má 7 prvků 
	  v1 = 9; // error: no převod z int vektoru  
	  vector <double> v2 = 9; // error: no převod z int vektoru  

	  void f (const vector <double> &); 
	  f (9); // error: no převod z int vektoru  

	  vector <double> v1 {7}; // ok: v1 má 1 element (s jeho hodnotou 7) 
	  v1 = {9}; // ok v1 má nyní 1 element (s jeho hodnotou 9) 
	  vector <double> v2 = {9}; // ok: v2 má 1 element (s jeho hodnotou 9) 
	  f ({9}); // ok: f se nazývá se seznamem {9} 

	  vektor <vector <double>> vs = { 
		  vector <double> (10), // ok: explicitní konstrukce (10 prvků) 
		  vector <double> {10}, // ok explicitní konstrukce (1 prvek s hodnotou 10) 
		  10 // error: vektoru konstruktor je výslovně 
	  }; 	

Funkce může přistupovat k initializer_list jako sekvence neměnné. Například:

 
	  void f (initializer_list <int> args) 
	  { 
		  pro (auto p = args.begin (); p = args.end ();! + + p) cout << * p << "\\\\n" ; 
	  } 

Konstruktor, který přijímá jeden argument typu std :: initializer_list se nazývá inicializace-seznam konstruktor.

Standardní knihovna kontejnery, řetězce, a regex mají initializer-seznam konstruktory, přiřazení, atd. initializer-list lze použít jako rozsahu, např. v rozmezí prohlášení

Inicializátor seznamy jsou součástí systému pro jednotné a obecné inicializaci .

Viz vzít


Prevence zúžení

Problém: C a C + + implicitně zkrátí:

 
	  int x = 7,3; // Ouch! 
	  void f (int); 
	  f (7,3); // Ouch! 

Nicméně, v jazyce C + 11, {} inicializace není úzký:

  int x0 {7,3}; // error: zúžení int x1 = {7,3}; // error: zúžení double d = 7; int x2 {d}; // error: zúžení (double na int) char x3 {7}; // ok: i když 7 je int, to není zužuje vektor <int> vi = {1, 2,3, 4, 5,6}; // error: double na int zúžení 

Způsob, jakým C + 11 zabraňuje hodně nekompatibilit je, opíraje se o skutečných hodnotách inicializaci (např. 7 ve výše uvedeném příkladu), i když tomu tak (a to nejen typu) při rozhodování, co je zúžení konverze. Pokud hodnota může být reprezentován přesně jako cílový typ, převod není zužuje.

	 char c1 {7}; // OK: 7 je int, ale vejde se do char
	 char c2 {77777}; // error: zúžení 

Všimněte si, že floating-point na celočíselné konverze jsou vždy považovány za zúžení - dokonce 7,0-7.

Viz vzít


Zmocňovací konstruktory

". Init () funkce" v jazyce C + 98, pokud chcete, aby dva konstruktory dělat stejnou věc, opakovat sami, nebo volejte Například:

 
	  class X { 
		  int; 
		  validate (int x) {if (0 <x && x <= max) = x, jinak vyhodit bad_X (x);} 
	  public: 
		  X (int x) {ověření (x);} 
		  X () {ověřovat (42);} 
		  X (string s) {int x = lexical_cast <int> (y), ověření (x);} 
		  // ... 
	  }; 

Upovídanost brání čitelnosti a opakování je náchylné k chybám. Jak dostat do cesty udržovatelnosti. Takže, v jazyce C + 11, lze definovat jeden konstruktor, pokud jde o další:

 
	  class X { 
		  int; 
	  public: 
		  X (int x) {if (0 <x && x <= max) = x, jinak vyhodit bad_X (x);} 
		  X (): X {42} {} 
		  X (string s): X {lexical_cast <int> (y)} {} 
		  // ... 
	  }; 

Viz vzít


V-class člen inicializátory

V C + 98, mohou být pouze statické const členy integrálních typů být inicializován ve své třídě, a initializer musí být konstantní výraz. . Tato omezení zajistí, že můžeme provést inicializaci při kompilaci Například:

 
	  int var = 7; 

	  class X { 
		  static const int m1 = 7; // ok 
		  const int m2 = 7; // error: není statická 
		  static int m3 = 7; // error: není const 
		  static const int m4 = var, // error: initializer není konstantní výraz 
		  static const string m5 = "odd"; // error: není nedílnou typ 
		  // ... 
	  }; 

Základní myšlenka pro C + 11 je umožnit non-statický datový člen být inicializována, kde je deklarován (ve své třídě). Konstruktor pak můžete použít inicializátor při spuštění-čas inicializace nutná Zvažte.:

 
	  class A { 
	  public: 
		  int = 7; 
	  }; 

To je ekvivalentní:

 
	  class A { 
	  public: 
		  int; 
		  (): (7) {} 
	  }; 

Tím se ušetří trochu psaní, ale skutečné výhody přijdou ve třídách s více konstruktorů. Často, všechny konstruktory používat společný inicializátor pro člena:

 
	  class A { 
	  public: 
		  (): (7), b (5), hash_algorithm ("MD5"), s ("konstruktor run") {} 
		  (Int a_val): (a_val), b (5), hash_algorithm ("MD5"), s ("konstruktor run") {} 
		  (D d): (7), b (g (d)), hash_algorithm ("MD5"), s ("konstruktor run") {} 
		  int a, b; 
	  soukromé: 
		  HashingFunction hash_algorithm; // Cryptographic Hash které mají být použity pro všechny A instance 
		  std :: string s; // String indikuje stav v objektu cyklu 
	  }; 

Skutečnost, že hash_algorithm a to každý z nich má jeden výchozí je ztracen ve změti kódu a snadno se může stát problémem při údržbě. Místo toho, můžeme vytknout inicializaci datových členů:

 
	  class A { 
	  public: 
		  (): (7), b (5) {} 
		  (Int a_val): (a_val), b (5) {} 
		  (D d): (7), b (g (d)) {} 
		  int a, b; 
	  soukromé: 
		  HashingFunction hash_algorithm {"MD5"}; // Cryptographic Hash které mají být použity pro všechny A instance 
		  std :: string s {"konstruktor run"}; // String s uvedením státu v objektu životního cyklu 
	  }; 

Pokud člen je inicializován jak in-třídy inicializátor a konstruktéra, pouze konstruktor je inicializace provádí (to "přepíše" výchozí). Takže můžeme dále zjednodušit:

 
	  class A { 
	  public: 
		  () {} 
		  (Int a_val): (a_val) {} 
		  (D d): b (g (d)) {} 
		  int = 7; 
		  int b = 5; 	
	  soukromé: 
		  HashingFunction hash_algorithm {"MD5"}; // Cryptographic Hash které mají být použity pro všechny A instance 
		  std :: string s {"konstruktor run"}; // String s uvedením státu v objektu životního cyklu 
      }; 

Viz vzít


Dědičné konstruktory

Lidé někdy jsou zmateni o tom, že obyčejní rozsah pravidla se vztahují na členy tříd. Zejména, člen základní třídy není ve stejném rozsahu jako člen odvozené třídě:

 
	  struct {B 
		  void f (double); 
	  }; 

	  struct D: B { 
		  void f (int); 
	  }; 

	  B b; bf (4.5); // jemné 
	  D d; df (4,5); // překvapení: volání f (int) s argumentem 4 

V C + 98, můžeme "výtah", sadu přetížených funkcí ze základní třídy do odvozené třídy:

 
	  struct {B 
		  void f (double); 
	  }; 

	  struct D: B { 
		  pomocí B :: f; // přivést všechny f () S od B do působnosti 
		  void f (int); // přidání nového f () 
	  }; 

	  B b; bf (4.5); // jemné 
	  D d; df (4,5); // jemné: hovory D :: f (double), který je B :: f (double) 

Řekl jsem, že "malý víc než historická nehoda zabrání pomocí této práci pro konstruktoru, stejně jako pro běžné členské funkce." C + 11 stanoví, že zařízení:

 
	  třída odvozená: public Základní {  
	  public:  
		  pomocí Base :: f; // výtahu základny f do odvozených tyto působnosti - pracuje v C + 98 
		  void f (char); // poskytnout novou f  
		  void f (int); // preferujete tuto f na základnu :: f (int)  

		  pomocí Base :: základny; // lift Základní konstruktory odvozených tyto rozsah - C + 11 pouze 
		  Odvozené (char); // poskytnout nový konstruktor  
		  Odvozené (int); // raději tento konstruktor Base :: Base (int)  
		  // ... 
	  };  
 

Pokud se tak rozhodnete, můžete stále střílet do nohy pomocí dědění konstruktory v odvozené třídě, ve které definují nové členské proměnné, kteří potřebují inicializaci:

 
	  struct {B1 
		  B1 (int) {} 
	  }; 

	  struct D1: B1 { 
		  pomocí B1 :: B1; // implicitně deklaruje D1 (int) 
		  int x; 
	  }; 

	  void test () 
	  { 
		  D1 d (6), // Jejda: dx není inicializován 
		  D1 e; // error: D1 nemá výchozí konstruktor 
	  } 

Můžete odstranit kulku z nohy pomocí člen-inicializátor :

 
    	  struct D1: B1 { 
    		  pomocí B1 :: B1; // implicitně deklaruje D1 (int) 
    		  int x {0}; // poznámka: x je inicializována 
    	  }; 

    	  void test () 
    	  { 
    		  D1 d (6) // dx je nulová 
    	  } 

Viz vzít


Statické (v době kompilace) tvrzení - static_assert

Statická (kompilaci) tvrzení se skládá z konstantní výraz a řetězec doslovný:

 
	  static_assert (výraz, string); 
 

Kompilátor vyhodnotí výraz a zapíše řetězec jako chybová zpráva, pokud je výraz nepravdivý (tj., pokud tvrzení nepodařilo) Například.:

 
	  static_assert (sizeof (long)> = 8, "64-bit generování kódu potřebné pro tuto knihovnu."); 
	  struct S {X m1, Y m2;}; 
	  static_assert (sizeof (S) == sizeof (X) + sizeof (Y), "neočekávaný polstrování S"); 
 

Static_assert může být užitečné, aby se předpoklady o programu a jeho léčby u překladače explicitní. Všimněte si, že static_assert je hodnocena v době kompilace, to nemůže být použita pro kontrolu předpokladů, které závisí na run-time hodnot Například.:

 
	  int f (int * p, int n) 
	  { 
		  static_assert (p == 0, "p není null"); // chyba: static_assert () výraz není konstantní výraz 
		  // ... 
	  } 

(Místo, testovat a vyvoláním výjimky v případě poruchy).

Viz vzít


dlouho, dlouho - déle integer

. Celé číslo, které je alespoň 64 bitů dlouhý Například:

 
	  long long x = 9223372036854775807LL; 
 

Ne, nejsou tam žádné dlouhé dlouho, dlouho s ani nemůže dlouho být napsány krátké long long.

Viz vzít


nullptr - null pointer doslovný

nullptr je doslovný označující nulový ukazatel, ale není celé číslo:

 
	  char * p = nullptr; 
	  int * q = nullptr; 
	  char * p2 = 0; // 0 stále pracuje a p == p2 

	  void f (int); 
	  void f (char *); 

	  f (0), // volání f (int) 
	  f (nullptr); // volání f (char *) 

	  void g (int); 
	  g (nullptr); // error: nullptr není int 
	  int i = nullptr; // error nullptr není int 

Viz vzít


Přípona Návratový typ syntaxe

Zvažte:

 
	  template <class T, třída U> 
	  ? Mul (T x, y U) 
	  { 
		  return x * y; 
	  } 

Co můžeme psát jako návratový typ? Je to "typ x * y", samozřejmě, ale jak můžeme říci, že? První nápad, použít decltype :

 
	  template <class T, třída U> 
	  decltype (x * y) mul (T x, U y) // rozsah problému! 
	  { 
		  return x * y; 
	  } 

To nebude fungovat, protože x a y nejsou v rozsahu. Nicméně, my můžeme psát:

 
	  template <class T, třída U> 
	  decltype (* (T *) (0) ** (U *) (0)) mul (T x, y U) // ošklivý!  a náchylné k chybám 
	  { 
		  return x * y; 
	  } 

Nicméně, volání, že "není dost" by příliš zdvořilé.

Řešení je kladen návratový typ, kam patří, po argumenty:

 
	  template <class T, třída U> 
	  auto mul (T x, U y) -> decltype (x * y) 
	  { 
		  return x * y; 
	  } 

Používáme notace auto znamená "návratový typ se odvozuje nebo upřesněny později."

Přípona syntaxe není primárně o šablonách a typu odpočet, je to opravdu o působnosti.

 
	  struct {List 
		  struct Link {/ * ...  * /}; 
		  Link * erase (Link * p) // odstranit p a vrátí odkaz před p 
		  // ... 
	  }; 

	  Seznam :: Link * List :: erase (Link * p) {/ * ...  * /} 
 

První seznam :: je nutné jen proto, že je rozsah seznamu není součástí až druhé Seznamu :: lepsi.:

 
	  auto List :: erase (Link * p) -> Link * {/ * ...  * /} 
 

Nyní ani Link potřebuje explicitní kvalifikaci.

Viz vzít

  • C + + Návrh sekce??
  • [Str02] Bjarne Stroustrup. Předloha návrhu na "typeof". C + + reflektor zpráva c + + std-ext-5364, říjen 2002.
  • [N1478 = 03-0061] Jaakko Jarvi, Bjarne Stroustrup, Douglas Gregor, a Jeremy Siek: Decltype a auto .
  • [N2445 = 07-0315] Jason Merrill: Nová funkce Declarator Syntaxe Formulace .
  • [N2825 = 09-0015] Lawrence Crowl a Alisdair Meredith: Unified Syntaxe funkce .

šablony alias (dříve známý jako "šablonu typedef")

Jak můžeme vytvořit šablonu, která je "stejně jako jiné šablony", ale pravděpodobně s několika šablon argumentů uvedených (vázané) Zvažte?:

 
	  šablona <class T> 
	  pomocí Vec = std :: vector <T, My_alloc <T>>; // standard vector pomocí mé alokátor 

	  Vec <int> fib = {1, 2, 3, 5, 8, 13}, // přidělí prvky pomocí My_alloc 

	  vektor <int, My_alloc <int>> verbose = fib; // verbose a fib jsou stejného typu 

Klíčové slovo pomocí se používá k získání lineární označením "jméno následované co se týká." Snažili jsme se s konvenčním a spletitý typedef řešení, ale nikdy se podařilo získat kompletní a koherentní řešení, dokud jsme se dohodli na méně obskurní syntaxe.

Specializace práce (můžete alias sada specializací, ale nemůžete se specializují aliasu) Například:

	 šablona <int>
	 struct int_exact_traits {// idea: int_exact_trait <N> :: typ je typ s přesně n bitů 
		  typedef int typ; 
	  }; 

	  template <> 
	  struct int_exact_traits <8> { 
		  typedef char typu; 
	  }; 

	  template <> 
	  struct int_exact_traits <16> { 
		  typedef char [2] Typ; 
	  }; 

	  // ... 

	  šablona <int n> 
	  pomocí int_exact = typename int_exact_traits <N> :: typ; // define alias pro pohodlné zápis 

	  int_exact <8> = 7; // int_exact <8> je int s 8 bitů 

Kromě toho, že důležitá v souvislosti s šablon, může typ aliasy také použít jako jiný (a IMO lepší) syntaxi běžných typů aliasy:

 
  typedef void (* PFD) (double); // C style 
  pomocí PF = void (*) (double); // pomocí Plus C-stylu typu 
  pomocí P = [] (double) -> void; // použití Plus přípony návratový typ 

Viz vzít

  • C + + Návrh: 14.6.7 Šablony pseudonymy; 7.1.3 typedef Specifikátor
  • [N1489 = 03-0072] Bjarne Stroustrup a Gabriel Dos Reis: Šablony aliasy pro C + + .
  • [N2258 = 07-0118] Gabriel Dos Reis a Bjarne Stroustrup: Šablony Aliasy (revize 3) (konečný návrh).

Variadic Šablony

Problémy je třeba řešit:

  • Jak postavit třídu s 1, 2, 3, 4, 5, 6, 7, 8, 9, nebo ... Inicializátory?
  • Jak se vyhnout konstrukci objektu z částí a pak kopírování výsledek?
  • Jak postavit tuple?

Poslední otázka je klíčem: Myslete tuple! Pokud můžete volat a přístup k obecné n-tic, zbytek bude následovat.

Zde je příklad (z "A stručném úvodu do Variadic šablony'' (viz odkazy)) prováděcího obecný, typ bezpečné, printf () Bylo by asi bylo lepší použít podporu :: formát, ale považují.:

 
	  const string pi = "pi"; 
	  const char * m = "hodnota% s je asi% g (pokud žijete v% s) \\\\n".; 
	  printf (m, pi, 3.14159, "Indiana"); 

 

Nejjednodušší případ printf () je, když tam nejsou žádné argumenty kromě formátovací řetězec, takže to zvládnu, že první:

 
	  void printf (const char * s) 	
	  { 
		  while (s && * s) { 
		 	  if (* s == '%' && * + + s! = '%') // aby se ujistil, že tam byl ne chtěl být více argumentů 
							  //%% Představuje prostý% ve formátu řetězce 
		           házet runtime_error ("neplatný formát: chybějící argumenty"); 
			  std :: cout << * s + +; 
		  } 
	  } 

To udělal, musíme zvládnout printf () s více argumenty:

 
	  template <typename T, typename ...  Args> // vědomí "..." 
	  void printf (const char * s, T hodnota, Args ... args) // na vědomí, "..." 
	  { 
		  while (s && * s) { 
			  if (* s == '%' && * + + s! = '%') {// specifikátor formátu (ignorovat, který z nich to je) 
				  std :: cout << hodnota; // použijte první non-Formát argument 
				  návrat printf (+ + s, args ...); // "odlepení" první argument 
			  } 
			  std :: cout << * s + +; 
		  } 
		  throw std :: Runtime Error ("extra argumenty poskytované printf"); 
	  } 

Tento kód jednoduše `` oddělí'' první non-formát argument a rekurzivně volá sama sebe. Pokud nejsou k dispozici žádné další non-format argumenty, volá první (jednodušší) printf () (viz výše). To je spíše standardní funkční programování provádí v době kompilace. Všimněte si, jak přetížení << nahrazuje použití (případně chybný) `` náznak'' ve formátu specifikátor.

Args ... definuje, co se nazývá `` parametr balík.'' To je v podstatě posloupnost (typ / hodnota) páry, z nichž si můžete `` odlepení'' argumenty začínající první. Když je printf () je volána s jedním argumentem, první definice (printf (const char *)) je vybrán. Když je printf () je volána se dvěma nebo více argumentů, druhý HD (printf (const char *, hodnota T, Args ... args)) je vybrán, s prvním argumentem jako s, druhý jako hodnotu, a zbytek (pokud existuje) svázané do args parametrů balení pro pozdější použití. Ve výzvě

 
	  printf (+ + s, args ...);  
 

V args parametrů balení je rozšířena tak, aby Následující argument lze nyní vybrat jako hodnotu. To přináší, dokud args je prázdný (tak, že se nazývá první printf ()).

Pokud jste obeznámeni s funkcionální programování, měli byste toto neobvyklý zápis pro hezké standardní technikou. Pokud ne, zde jsou některé drobné technické příkladů, které by mohly pomoci. Nejprve můžeme deklarovat a používat jednoduché variadic šablony funkce (stejně jako printf () výše):

 
	  šablony <class ...  Typy>  
		  void f (typy ... args); // variadic funkce šablon 
					  // (Tj. funkci, která může trvat libovolný počet argumentů libovolné typy) 
	  f (); // OK: args neobsahuje žádné argumenty 
	  f (1); // OK: args obsahuje jeden argument: int 
	  f (2, 1,0); // OK: args obsahuje dva argumenty: int a double 

Můžeme vybudovat variadic typ:

 	
	  template <TypeName Head, typename ...  Tail> 
	  třída n-tice <Head, Tail...> 
		  : Soukromá n-tice <Tail...> {// zde je rekurze 
				  // V podstatě, tuple ukládá svou hlavu (první (typ / hodnota) pár  
				  // A pochází z n-tice svého ocasu (zbytek (typ / hodnota) páry. 
				  // Všimněte si, že typ je zakódován v typu, nejsou uloženy jako data 
		  typedef tuple <Tail...> dědictvím; 
	  public: 
		  n-tice () {} // default: prázdná n-tice 

		  // Construct tici z oddělených argumentů: 
		  n-tice (TypeName add_const_reference <head> :: Typ v., typename add_const_reference <Tail> :: typ ... vtail) 
			  : M_head (v), zdědil (vtail. ..) {} 

		  // Construct tici z jiného tice: 
		  šablony <TypeName ...  VValues> 
		  n-tice (tuple const <VValues...> & other) 
		  : M_head (other.head ()), zdědil (other.tail ()) {} 

		  šablony <TypeName ...  VValues> 
		  tice & operator = (const tuple <VValues...> a jiné) // přiřazení 
		  { 
			  m_head = other.head (); 
			  ocas () = other.tail (); 
			  návrat * tato; 
		  } 

		  TypeName add_reference <HEAD> :: typ hlavy () {return m_head;} 
		  TypeName add_reference <const Head> :: typ hlavy () const {return m_head;} 

		  zdědil a ocas () {return * this;} 
		  const zdědil a ocas () const {return * this;} 
	  chráněna: 
		  Vedoucí m_head; 
	  } 

Vzhledem k tomu, že definice, můžeme n-tice (a kopírování a manipulovat s nimi):

 
	  tice <string, vektor   , Double> tt ("ahoj", {1,2,3,4}, 1,2); 
	  string h = tt.head (); // "ahoj" 
	  tice <vector <int>, double> t2 = tt.tail (); // {{1,2,3,4}, 1,2}; 

To může být trochu únavné zmínit všechny z těchto typů, tak často, odvodíme je z argumentů typu, např. pomocí standardní knihovny make_tuple ():

 
	  šablony <class ...  Typy> 
	  tice <Types...> make_tuple (typu && ... t) // tato definice je poněkud zjednodušená (viz norma 20.5.2.2) 
	  { 
		  návrat n-tice <Types...> (T. ..); 
	  } 
	
	  string s = "Hello"; 
	  vector <int> v = {1,22,3,4,5}; 
	  auto x = make_tuple (s, v, 1.2); 
 

Viz brát:


Jednotná inicializace syntaxe a sémantika

C + + nabízí několik způsobů inicializace objektu v závislosti na jeho typu a inicializace kontextu. Při zneužití, může být chyba překvapující a chybové zprávy temný Zvažte.:

 
	  string [] = {"foo", "bar"}; // ok: inicializovat pole variabilní 
	  vector <string> v = {"foo", "bar"}; // error: inicializátor seznamu non-aggregate vektoru 
	  void f (string []); 
	  f ({"foo", "bar"}); // chyba syntaxe: blok jako argument 

 

 
	  int = 2; // "přiřazení stylu" 
	  int aa [] = {2, 3}, // přiřazení stylu se seznamem 
	  Komplex z (1,2); // "funkční styl" inicializace 
	  x = Ptr (y); // "funkční styl" pro převod / cast / konstrukce 

 

 
	  int (1); // definice proměnné 
	  int b (); // Deklarace funkce 
	  int b (foo); // definice proměnných nebo funkcí prohlášení 

To může být těžké si vzpomenout, pravidla pro inicializaci a vybrat si nejlepší způsob.

C + 11 řešením je umožnit, aby {}-inicializátor seznamy pro všechny inicializaci:

 
	  X x1 = X {1,2};  
	  X x2 = {1,2}, // = je volitelné 
	  X x3 {1,2};  
	  X * p = new X {1,2};  

	  struct D: X { 
		  D (int x, int y): X {x, y} {/ * ...  * /}; 
	  }; 

	  struct S { 
		  int [3]; 
		  S (int x, int y, int z): {x, y, z} {/ * ...  * /}; // Řešení starého problému 
	  }; 

Důležité je, že X {} vytvoří stejnou hodnotu ve všech souvislostech, tak, že {} inicializace dává stejný výsledek ve všech místech, kde to je legální Například.:

 
	  X x {};  
	  X * p = new X {}; 
	  z = X {}; // použití jako obsazení 
	  f ({}); // funkce argument (typu X) 
	  návrat {}; // návratová hodnota funkce (funkce vracející X) 

Viz vzít


Rvalue odkazy

Rozdíl mezi lvalues ​​(co lze použít na levé straně přiřazení) a rvalues ​​(co lze použít na pravé straně přiřazení) sahá až do Christopherem Strachey (otec C + + 's vzdáleného předka CPL a z Denotační sémantiky). V C + +, non-const odkazy vážou na lvalues, const odkazy na lvalues ​​nebo rvalues, ale není nic, co se může vázat na non-const rvalue. To je chránit lidi před změnou hodnot provizorií, které jsou zničeny dříve, než jejich nová hodnota může být použita Například.:

 
	  void zvýš (int &) {+ +;} 
	  int i = 0; 
	  zvýš (i); // i se 1 
	  zvýš (0); // error: 0 v ne na lvalue 

Pokud to zvýš (0) byly povoleny ani některé dočasné, že nikdo nikdy neviděl by být zvýšen nebo - mnohem horší - hodnota 0 by se stal 1. Ten zní hloupě, ale byl vlastně chyba, jako že v raných překladače Fortran, který zrušil umístění v paměti držet hodnotu 0.

Tak daleko, tak dobrý, ale za to,

 
	  šablona <class T> swap (T & T & b) // "old style swap" 
	  { 
		  T tmp (); // nyní máme dvě kopie 
		  = b; // nyní máme dvě kopie b 
		  b = tmp; // nyní máme dvě kopie tmp (aka) 
	  }  

Pokud T je typ pro které to může být drahé zkopírovat prvky, jako například řetězec a směr, swap stává náročná operace (pro standardní knihovny, máme specializace řetězec a vektoru swap (), aby se s tím vypořádat). Poznámka něco podivného: Nechtěli jsme žádné kopie vůbec. Jen jsme chtěli přesunout hodnoty a, b, a tmp kolem trochu.

V C + 11, můžeme definovat "Přesunout konstruktory" a "Přesunout úkoly", aby se přesunuli spíše než kopírovat svůj argument:

 
	  template <class T> class vector { 
		  // ... 
		  vektor (vector const &); // kopírovací konstruktor 
		  vektor (vektor &&); // tah konstruktor 
		  vector & operator = (const vector &); // copy přiřazení 
		  vector & operator = (vektorová &&); // tah přiřazení 
	  }; // Poznámka: pohyb konstruktér a pohyb postoupením non-const && 
		  // Oni mohou, a obvykle se, pište na svého tvrzení 

V && označuje "rvalue odkaz". Referenční rvalue se může vázat na rvalue (ale ne na lvalue):

 
	  X; 
	  X f (); 
	  X & r1 =; // bind r1 do (s lvalue) 
	  X & r2 = f (); // error: f () je rvalue, nemůže vázat 

	  X && RR1 = f (); // jemné: bind RR1 na dočasné 
	  X && RR2 =; // error: bind je lvalue 

Myšlenka přesunu práce je to, že namísto toho, aby kopii, to prostě má zastoupení od jejího pramene a nahradí jej s levným třídy. Například, pro řetězce s1 = s2 pomocí přesunutí úkolu by vytvořit kopii s2 's postavami, místo toho by to prostě nechat s1 zacházet s těmito znaky jako jeho vlastní, a tak nějak odstranit s1' s staré znaky (možná ponecháním v s2, který pravděpodobně jsou jen na pokraji zničení).

Jak můžeme vědět, zda je to v pořádku, prostě se ze zdroje? Řekneme kompilátoru:

 
	  šablona <class T>  
	  void swap (T & T & b) // "dokonalý swap" (téměř) 
	  { 
		  T tmp = tah (a); // může zneplatnit 
		  move = (b); // mohlo vyvrátit b 
		  b = move (tmp); // mohlo vyvrátit tmp 
	  } 

pohybovat (x) znamená "můžete léčit x jako rvalue". Možná, že by bylo lepší, kdyby pohyb () byl jmenován rval (), ale nyní přesunout () se používá pro let. Move () funkce šablon lze zapsat v C + odkazy 11 (viz "krátký úvod") a a používá rvalue.

Rvalue odkazy mohou být také použity pro dokonalé předávání.

V C + 11 knihovny standardní, jsou všechny nádoby opatřeny tah konstruktérů a posunou přidělování a operace, které vložíte nové prvky, jako například insert () a push_back () mají verze, které berou rvalue odkazy. Výsledkem je, že standardní kontejnery a algoritmy tiše - bez zásahu uživatele - zlepšení výkonu, protože kopírovat méně.

Viz vzít


svazy (generalizovaná)

V C + 98 (stejně jako v předchozích verzích C + +), může člen s uživatelem definovanou konstruktér, destruktor, nebo přiřazení nemůže být členem svazku:

 
	  union U { 
	  int m1; 
	  Komplex <double> m2; // error (hloupé): Komplex má konstruktor 
	  string m3; // error (není hloupý): string má vážné invariant 
				  // Udržovaný ctor, kopírování a dtor 
  }; 

Zejména

 
	  U u, // které konstruktoru, pokud nějaké byly? 
	  u.m1 = 1; // přiřadit k int člena 
	  string s = u.m3; // katastrofa: čtení z řetězce člena 

Je zřejmé, že je to nezákonné psát jednoho člena a pak si přečtěte další, ale lidé, které však (obvykle omylem).

C + 11 upravuje omezení odborů, aby se více typy prvků proveditelné, zejména, že umožňuje členskému typů s konstruktory a destruktory. Přidává také omezení, aby se více flexibilní odbory méně náchylné k chybám tím, že podporují budování diskriminovaných odborů.

Unie typy prvků jsou omezeny:

  • Žádné virtuální funkce (jako vždy)
  • Žádné reference (jako vždy)
  • Ne základnám (jako vždy)
  • Pokud unie má člen s uživatelsky definované konstruktér, kopírování, nebo destruktor pak, že zvláštní funkce je odstraněn ; to znamená, že to nemůže být použit pro objekt unijní typu. To je nová.

Například:

 
	  unie U1 { 
		  int m1; 
		  Komplex <double> m2; // ok 
	  }; 
	
	  unie U2 { 
		  int m1; 
		  string m3; // ok 
	  }; 

To může vypadat chyba-prone, ale nová omezení pomáhá. Zejména:

 
	  U1 u; // ok 
	  u.m2 = {1,2}; // ok: přiřadit komplexní člena 
	  U2 u2; // error: string destruktor způsobil destruktor U které mají být odstraněny 
	  U2 U3 = u2; // error: string copy konstruktor způsobil kopie U konstruktoru být odstraněny 

V podstatě, U2 je k ničemu, pokud vložíte do struct, který uchovává informace, které (varianta) člen je použit. Takže, stavět diskriminovat odbory, jako například:

 
	  třída Widget {// tři alternativy implementace reprezentován jako unie 
	  soukromé: 
		  enum class Tag {point, číslo, text} typ; // discriminant 
		  unie {// zastoupení 
			  Bod p; // bod má konstruktor 
			  int i; 
			  string s; // řetězec má výchozí konstruktor, kopírování operace, a destruktor 
		  }; 
		  // ... 
		  Widget & operator = (const widgetu & w) // nutné z důvodu řetězce varianty 
		  { 
			  if (typ == Tag :: textové && w.type == Tag :: text) { 
				  s = ws; // obvykle string přiřazení 
				  návrat * tato; 
			  } 

			  if (typ == Tag :: text) s ~ string ();. // zničit (explicitly!) 

			  switch (typ == w.type) { 
			  Případ Tag :: bod: p = wp break; // normální kopie 
			  Případ Tag :: Číslo: i = wi break; 
			  Případ Tag :: text: nový (a y) (ws) break; // umístění nových 
			  } 
			  type = w.type; 
			  návrat * tato; 
		  } 
	  }; 

Viz brát:

    li> C + + Návrh část 9.5
  • [N2544 = 08-0054] Alan Talbot, Lois Goldthwaite, Lawrence Crowl, a Jens Maurer: Neomezená svazy (revize 2)

POD (generalizované)

POD ("Plain Old Data") je něco, co lze manipulovat jako C struct, např. kopie s memcpy (), inicializuje se memset (), atd. V C je + 98 samotná definice POD založeno na souboru omezení týkajících se používání jazykových prvků použitých v definici struct:

 
	  struct S {int a;}; // S je POD 
	  struct SS {int, SS (int aa): (aa) {}} // SS není POD 
	  struct {SSS virtual void f (); / * ...  * /}; 
 

V C + 11, S a SS jsou "standardní rozložení typy" (aka POD), protože tam je opravdu nic "kouzlo" o SS: konstruktor nemá vliv na rozložení (tak memcpy () by bylo v pořádku), pouze inicializace Pravidla (memset () by bylo špatné - ne prosadit invariant). Nicméně, bude SSS ještě vložený vptr a nebude nic jako "obyčejné údajů." C + 11 definuje POD, triviálně copyable typy, triviální typy a standardní rozložení typy se vypořádat s různými technickými aspekty toho, co bývala POD. POD je definována rekurzivně

  • Pokud jsou všechny vaše členy a základy jsou POD, jsi POD
  • Jako obvykle (podrobnosti jsou uvedeny v kapitole 9 [10])
    • Žádné virtuální funkce
    • Žádné virtuální základy
    • Žádné reference
    • No vícenásobný přístup Specifikátory

Nejdůležitějším aspektem PODy C + 11 arů, že přidáním nebo odebráním konstruktory nemají vliv na rozložení nebo výkon.

Viz brát:


Surové řetězcové literály

V mnoha případech, například při psaní regulárních výrazů pro použití se standardním regex knihovny, skutečnost, že zpětné lomítko (\\\\) je znak escape je velké problémy (protože v regulárních výrazech je zpětné lomítko slouží k zavedení speciální znaky představující znak třídy). Zvažte, jak psát vzor představující dvě slova oddělená lomítkem (\\\\ \\\\w \\\\ w):

 
	  string s =  // Doufám, že jsem dostal, že právo 

Všimněte si, že znak zpětného lomítka je reprezentován jako dvěma zpětnými lomítky v regulárním výrazu. V podstatě, "surový řetězec literálu" je řetězcový literál, kde zpětné lomítko je jen zpětné lomítko, takže náš příklad stává:

 
	  string s = R "(\\\\w \\\\w) ", // Jsem si jistý, že jsem dostal, že právo 

Původní návrh na surové řetězce představuje to jako motivační příklad

 
      // Jsou pět zpětná lomítka správné, nebo ne? 
							  // Dokonce i odborníci se snadno zaměnit.  

R "(...)" zápis je trochu více mnohomluvný než "holý" "...", ale "něco víc" je nutné, pokud nemáte escape znak: Jak si dát nabídku do surový řetězec? Snadné, pokud předchází):

 
	  R "(" řetězec v uvozovkách ")" // string je "citoval řetězec" 

Tak, jak se dostat posloupnost znaků) "do syrové řetězce? Naštěstí, že je to vzácný problém, ale" (...) "je pouze výchozí oddělovač pár. Můžeme přidat oddělovače před a po (... ) v "(...)". Například

 
	  R "*** (" citoval řetězec obsahující obvyklé terminátor (")") *** "// string je" citoval řetězec obsahující obvyklé terminátor (")" 

Posloupnost znaků po) musí být shodný s sekvenci před (. Tímto způsobem můžeme vyrovnat s (téměř) libovolně složitých vzorů.

Počáteční R syrové řetězce lze předcházet kódování-předpona: u8, U, U, nebo L. Například u8R "(fdfdfa)" je UTF-8 řetězec literálu.

Vidět


Uživatelem definované literály

C + + poskytuje literály pro různé předdefinované typy (2,14 Literály):

 
	  123 // int 
	  1,2 // double 
	  1.2F // float 
	  "" // Char 
	  1ULL // unsigned long long 
	  0xD0 // šestnáctkové unsigned 
	  "Jako" // string 

Nicméně, v C + 98 nejsou literály pro uživatelem definované typy. To může být na obtíž, a také spatřovat porušení zásady, že uživatelem definované typy by měly být podporovány, stejně jako vestavěné typy. Zejména, lidé požadují:

 
	  "Ahoj!" S // string, ne `` nula zakončený pole char'' 
	  1.2i // imaginární 
	  123.4567891234df // desetinné s plovoucí desetinnou čárkou (IBM) 
	  101010111000101b // binární 
	  123s // sekund 
	  123,56 km // není km!  (Jednotky) 
	  1234567890123456789012345678901234567890x // rozšířený přesné 

C + 11 podporuje `` uživatelem definované literály'' přes ponětí o doslovný Provozovatel je to za mapu literály s danou příponou do požadovaného typu Například.:

 
	  constexpr komplex <double> provozovatel "" i (dlouhý double d) // imaginární literal 
	  { 
		  return {0, d}, // areálu   je doslovný typu 
	  } 

	  std :: string operátor "" s (const char * p, size_t n) // std :: string literal 
	  { 
		  návrat řetězec (p, n); // vyžaduje volný obchod přidělování 
	  } 

Všimněte si použití constexpr umožňující v době kompilace hodnocení. Vzhledem k tomu, ty, můžeme napsat

 
	  template <class T> void f (const T &); 
	  f ("Hello"); // pass ukazatel na char * 
	  f ("Hello" s); // pass (5 znaků) objekt typu string 
	  f ("Hello \\\\n" s); // pass (6 znaků) objekt typu string 

	  auto z = 2 +1 i; // areálu   (2,1) 

Základní (implementace) idea je, že po analýze toho, co by mohlo být doslovný, kompilátor vždy zkontrolujte, zda příponou. Uživatelem definované literal mechanismus jednoduše umožňuje uživateli zadat nový příponu a co je třeba udělat s doslovným před ním. To není možné předefinovat význam vestavěným doslovném příponou nebo rozšířit syntaxi literály. Doslovný Provozovatel může požádat, aby se jeho (předchozí) doslovný passed `` vařené'' (s hodnotou by musela v případě, že nová přípona nebyla definována), nebo `` tepelně'' (jako řetězec).

Chcete-li získat `` tepelně neupravené'' řetězec, jednoduše požádat o jediné const char * argument:

 
	  Bignum operátor "" x (const char * p) 
	  { 
		  návrat Bignum (p); 
	  } 

	  void f (Bignum); 
	  f (1234567890123456789012345678901234567890x); 
 

Zde C-styl string "1234567890123456789012345678901234567890" je předán provozovateli "" x (). Všimněte si, že jsme neměli výslovně dal trojčíslí do řetězce.

Existují čtyři druhy literálů, které mohou být suffixed aby uživatelsky definované literal

  • integer literál: přijat doslovným provozovatelem, přičemž jeden unsigned long long nebo const char * argument.
  • floating-point literal: přijat doslovným provozovatelem, přičemž jeden dlouhý dvojité nebo const char * argument.
  • řetězcový literál přijata doslovným provozovatelem, přičemž dvojice (const char *, size_t) argumenty.
  • znak literál přijata doslovným provozovatelem, přičemž jeden char argument.

Všimněte si, že nemůžete udělat doslovný operátor pro řetězec literálu, který trvá jen const char * argumentu (a ne velikost) Například.:

 
	  string operátor "" S (const char * p) // varování: to nebude fungovat podle očekávání 

	  "Jedna dvě" S; // error: no použitelný literal Provozovatel 

Důvodem je, že pokud chceme mít `` jiný druh řetězce'' my téměř vždy chtějí vědět, počet znaků stejně.

Přípony bude mít tendenci být krátké (např. s pro řetězec, i pro imaginární, m pro přístroj, a x pro rozšířené), může tak různé použití snadno střetnout. Použití názvů, aby se zabránilo střetům:

 
	  namespace Numerics {  
		  // ... 
		  class Bignum {/ * ...  * /};  
		  namespace literály {  
			  Provozovatel "" X (char const *);  
		  }  
	  }  

	  using namespace Numerics :: literály;  
 

Viz brát:


Atributy

`` Atributy'' je nový standard syntaxe zaměřeny na poskytování nějaký pořádek v jídelně zařízení pro přidání volitelné a / nebo dodavatele konkrétní informace do zdrojového kódu (např. __ attribute__, __ declspec, a # pragma). C + 11 atributy se liší od stávajících syntaxí tím, že je použitelná v podstatě všude v kódu a vždy týkajících se bezprostředně předcházející syntaktické jednotky Například.:

	 void f [[noreturn]] () // f () se už nikdy nevrátí
	  {
		 házet "chyba"; // OK
	  }
	
	 struct foo * f [[carries_dependency]] (int i); // nápověda k optimalizaci
	 int * g (int * x, int * y [[carries_dependency]]);

Jak můžete vidět, je atribut umístěn do dvojitých hranatých závorkách: [[... ]]. Noreturn a carries_dependency jsou dva atributy definované v normě.

K dispozici je přiměřené obava, že vlastnosti budou použity pro vytvoření jazykové dialekty. Doporučení je použít atributy pouze kontrolovat věci, které nemají vliv na význam programu, ale může pomoci odhalit chyby (např. [[noreturn]]) nebo pomoci optimalizátory (např. [[carries_dependency]]).

Jeden plánované použití pro atributy je vylepšenou podporu pro OpenMP. Například:

	 pro [[omp :: paralelně ()]] (int i = 0; i <v.size (); + + i) {
		  // ...
	  }

Jak je uvedeno, lze atributy být kvalifikován.

Viz brát:

  • Standardní: 7.6.1 Atribut syntaxe a sémantika, 7.6.3-4 noreturn, carries_dependency 8 Declarators, 9 třídy, 10 Odvozené třídy, 12.3.2 konverzních funkcí
  • [N2418 = 07-027] Jens Maurer, Michael Wong: Směrem k podpoře pro atributy v C + + (revize 3)

Lambdy

Lambda výraz je mechanismus pro určení funkce objektu. Primární použití pro lambda je stanovit jednoduchou akci, které má provést nějakou funkci Například.:

 
	  vektor <int> v = {50, -10, 20, -30}; 

	  std :: sort (v.begin (), v.end ()); // výchozí řazení 
	  // Nyní v by měla být {-30, -10, 20, 50} 

	  // Řadit podle absolutní hodnoty: 
	  std :: sort (v.begin (), v.end (), [] (int, int b) {return abs () <abs (b);}); 
	  // Nyní v by měla být {-10, 20, -30, 50} 

Argument [] (int, int b) {return abs () <abs (b);} je "lambda" (nebo "lambda funkce" nebo "lambda výraz"), který určuje operaci, která dané dvou celočíselných argumenty a a b vrátí výsledek porovná jejich absolutní hodnoty.

Lambda výraz může získat přístup k lokální proměnné v rozsahu, ve kterém je použita. Například:

 
	  void f (vector <Record> & v) 
	  { 
		  vektorové <int> indexy (v.size ()); 
		  int count = 0; 
		  generovat (indices.begin (), indices.end (), [& count] () {return count + +;}); 

		  // Druh indexy v pořadí určeném poli názvu záznamů: 
		  std :: sort (indices.begin (), indices.end (), [a] (int, int b) {return v [] name <v. [b] name;..}); 
		  // ... 
	  } 

Někteří to považují za "opravdu čistý!"; Jiní vidí to jako způsob, jak psát nebezpečně obskurní kód. IMO, oba mají pravdu.

[&] Je "capture list" s uvedením, že místní použité názvy budou předány odkazem. Mohli bychom říci, že jsme chtěli, aby "zachytit" pouze v, mohli jsme to řekl: [& v]. Kdybychom chtěli předat V hodnotou, mohli jsme to řekl: [= v]. Zachycení nic není [], zachytit všechny odkazy je [&], a zachytit všechny hodnotou je [=].

Pokud akce není ani obyčejný, ani jednoduchý, doporučuji používat pojmenované funkce objektu nebo funkci. Například, nad Příkladem může být napsáno:

 
	  void f (vector <Record> & v) 
	  { 
		  vektorové <int> indexy (v.size ()); 
		  int count = 0; 
		  generovat (indices.begin (), indices.end (), [a] () {return + + pocet;}); 

		  struct {Cmp_names 
			  const vector <Record> & vr; 
			  Cmp_names (const vector <Record> a r): vr (r) {} 
			  bool operator () (int, int b) const {return vr [] <vr [b];} 
		  }; 

		  // Druh indexy v pořadí určeném poli názvu záznamů: 
		  std :: sort (indices.begin (), indices.end (), Cmp_names (v)); 
		  // ... 
	  } 

Pro malé funkce, jako je například tohoto pole Název záznamu srovnání, funkce objekt je zápis verbose, ale generovaný kód je pravděpodobné, že bude identické. V C + 98, takové funkční objekty musely být nelokální které mají být použity jako šablony tvrzení, v jazyce C + + to již není nutné .

Chcete-li zadat lambda musíte poskytnout

  • jeho zajetí Seznam: seznam proměnných je možné použít (kromě svých argumentů), pokud existuje ([a] význam "všechny místní proměnné předávány odkazem" v příkladu záznamu srovnání). Pokud žádná jména je třeba zpracovat, lambda začíná holý [].
  • (Volitelně) její argumenty a jejich typy (např., (int, int b))
  • Akce, která se provádí za blokem (např. {návrat v [a] název <v [b] název;..}).
  • (Volitelně) návratový typ pomocí nové přípony návratový typ syntaxe , ale obvykle jsme jen odvodit návratový typ z return. Není-li hodnota je vrácena void je odvozen.

Viz brát:


Místní druhy jako šablonu argumenty

V C + 98, by místní a nejmenovaný typy nelze použít jako šablony argumenty. To by mohlo být na obtíž, tak C + 11 vleky omezení:

 
	  void f (vector <X> & v) 
	  { 
		  struct Méně { 
			  bool operator () (const X &, const X & b) {return AV <bv;} 
		  }; 
		  sort (v.begin (), v.end (), méně ()); // C + 98: error: Méně je místní 
							  // C + 11: ok 
	  } 

V C + 11, máme také alternativu s použitím lambda výraz :

 
	  void f (vector <X> & v) 
	  { 
	 	  sort (v.begin (), v.end (),  
		        [] (Const X &, const X & b) {return prospekt <bv;}); // C + 11 
	  } 

Stojí za připomenutí, že pojmenování opatření mohou být velmi užitečné pro dokumentaci a povzbuzení k dobrému designu. Také, non-local (nutně jmenován) entity znovu.

C + 11 rovněž umožňuje hodnoty nepojmenovaných typů které mají být použity jako šablony argumenty:

 
	  šablona <typename T> void foo (T const & t) {} 
	  enum X {x}; 
	  enum {y}; 

	  int main () 
	  { 
		  foo (x); // C + 98: ok, C + 11: ok 
		  foo (y); // C + 98: error; C + 11: ok 
		  enum Z {z}; 
		  foo (z); // C + 98: error; C + 11: ok  
	  } 

Viz brát:


noexcept - prevence výjimka šíření

Pokud funkce nemůže vyvolat výjimku, nebo pokud program není napsán pro zpracování výjimky vyvolané funkcí, které funkce může být prohlášen noexcept Například.:

 
	  extern "C" double sqrt (double) noexcept; // nikdy hodit 

	  vector <double> my_computation (const vector <double> & v) noexcept // nejsem připraven zvládnout paměti vyčerpání 
	  { 
		  vector <double> res (v.size ()); // může hodit 
		  for (int i; i <v.size (); + + i) res [i] = sqrt (v [i]); 
		  návrat res; 
	  } 

Pokud funkce deklarovaná noexcept hody (tak, že výjimka se snaží uniknout noexcept funkce) program je ukončen (pomocí volání ukončit ()). Volání funkce terminate () nelze spoléhat na objekty, které jsou v přesně vymezených států (tj. není-li žádné záruky, že destruktory byly použity, žádné zaručené zásobník odvíjení, a žádná možnost pro obnovení programu, jako kdyby žádný problém se vyskytly). To je úmyslné a dělá noexcept jednoduchý, hrubý, a velmi účinný mechanismus (mnohem účinnější než starý dynamický throw () mechanismu).

Je to možná, aby se funkce podmíněně noexcept. Například, může algoritmus uvedeno, že se noexcept, pokud (a pouze pokud) operace, které používá na šablony argumentu jsou noexcept:

 
	  šablona <class T> 
	  void do_f (vector <T> & v) noexcept (noexcept (f (v.at (0)))) // může hodit, pokud f (v.at (0)) lze 
	  { 
		  for (int i; i <v.size (); + + i) 
			  v.at (i) = f (v.at (i)); 
	  } 

Zde jsem nejprve pomocí noexcept jako operátor: noexcept (f (v.at (0))) platí v případě, f (v.at (0)) nemůže hodit, že je-li f () a při () použity, jsou noexcept.

Noexcept () Provozovatel je konstantní výraz a nehodnotí svůj operand.

Obecná forma prohlášení noexcept je noexcept (výraz) a `` holý noexcept'' je prostě zkratka pro noexcept (true). Všechna prohlášení o funkci, musí mít kompatibilní noexcept specifikace.

Destruktor by neměl hodit, generované destruktor je implicitně noexcept (nezávisle na jaký kód je v jeho těle), pokud všichni členové ve své třídě se noexcept destruktory.

To je typicky špatný nápad mít operace přesunutí hod, takže prohlásit ty noexcept tam kde je to možné. Generované kopie nebo operace přesunu je implicitně noexcept, pokud všechny zkopírovat nebo přesunout operací používá na členy své třídy mají noexcept destruktory.

noexcept je široce a systematicky používány ve standardní knihovně ke zlepšení výkonnosti a vyjasnit požadavky. Viz brát:


zarovnání

Občas, zvláště když jsme psaní kódu, který manipulovat syrové paměti, musíme zadat požadovanou úpravu pro některé přidělení Například.:

 
	  alignas (double) unsigned char c [1024]; // pole znaků, vhodně sladěna pro čtyřhru 
	  alignas (16) char [100]; // zarovnání na 16 bajtů hranici 

K dispozici je také alignof operátor, který vrací zarovnání svého argumentu (který musí být typu). Například

 
	  constexpr int n = alignof (int); // ints jsou zarovnány na hranice bajtů n 

Viz brát:


Přepsat ovládacích prvků: override

Žádné speciální klíčové slovo nebo anotace je potřeba pro funkci v odvozené třídě potlačí funkci v základní třídě Například.:

 
	  struct {B 
		  virtual void f (); 
		  virtual void g () const; 
		  virtual void h (char); 
		  void k (); // není virtuální 
	  }; 

	  struct D: B { 
		  void f (); // přepíše B :: f () 
		  void g (); // nepotlačí B :: g () (špatný typ) 
		  virtual void h (char); // přepíše B :: h () 
		  void k (); // nepotlačí B :: k () (B :: k () není virtuální) 
	  }; 

To může způsobit zmatek (co jste programátor znamená?, A problémy, pokud kompilátor nevaruje před podezřelou kódu. Například,

  • Bylo programátor znamená přepsat B :: g ()? (Téměř jistě ano).
  • Bylo programování znamená přepsat B :: H (char)? (Pravděpodobně z důvodu nadbytečného explicitního virtuální).
  • Bylo programátor znamená přepsat B :: k. ()? (Pravděpodobně, ale to není možné).

Chcete-li povolit programátor být více explicitní o přepsání, nyní máme "kontextuální klíčové slovo" přepsání:

 
	  struct D: B { 
		  void f () přepsat; // OK: přepíše B :: f () 
		  void g () ovládání; // error: špatný typ 
		  virtual void h (char); // přepíše B :: h (); pravděpodobně varování 
		  void k () ovládání; // error: B :: k () není virtuální 
	  }; 

Prohlášení označené override je platná pouze v případě, že je funkce přepsat. Problém s h () ​​není zaručeno, že se zachytil (protože není chyba v závislosti na jazykové definice), ale že je snadno diagnostikována.

override je pouze kontextuální klíčové slovo, takže můžete i nadále používat jako identifikátor:

 
  int override = 7; // nedoporučuje 

Viz brát:


Přepsat ovládacích prvků: v konečném znění

Někdy, programátor chce, aby se zabránilo virtuální funkci od bytí přepsání. Toho lze dosáhnout tím, že přidá specifikátor poslední Například.:

 
	  struct {B 
		  virtual void f () const v konečném znění; // nepotlačí 
		  virtual void g (); 
	  }; 

	  struct D: B { 
		  void f () const; // error: D :: f pokusy přepsat finále B :: f 
		  void g (); // OK 
	  }; 

Existují legitimní důvody, proč chtějí, aby se zabránilo přepsání, ale obávám se, že většina příkladů jsem se ukázaly prokázat potřebu finále byly založeny na chybných předpokladech o tom, jak drahé virtuální funkce jsou (obvykle na základě zkušeností s jinými jazyky). Takže, pokud máte pocit, nutkání přidat konečné specifikátor, prosím zkontrolujte, že důvodem je logické: Chtěli sémantické chyby je pravděpodobný, pokud někdo definovat třídu, která přepsala že virtuální funkci? Přidání kolo uzavře možnost budoucího uživatele třída by mohla poskytnout lepší implementaci funkce pro některé třídy jste myslel na. Pokud nechcete, aby tato možnost otevřená, proč jsi definovat funkci, která bude virtuální na prvním místě? Většina rozumných odpovědi na tuto otázku, že jsem se setkal byli podél linií: Toto je základní funkce v rámci, který rámcové stavitelé potřebné přepsat, ale není bezpečné pro běžné uživatele přepsat. Můj zkreslení je být podezřívavý vůči těmto pohledávkám.

Pokud je výkon (inlining), které chcete, nebo si prostě nikdy chcete změnit, je většinou lepší není definovat funkci, která se virtuální na prvním místě. To není Java.

v konečném znění je pouze kontextuální klíčové slovo, takže můžete i nadále používat jako identifikátor:

 
  int kolo = 7; // nedoporučuje 

Viz brát:

  • Standardní: 10 Odvozené třídy [class.derived] [9]
  • Standardní: 10,3 Virtuální funkce [class.virtual]

C99 funkce

Chcete-li zachovat vysoký stupeň kompatibility, byly jen několik drobných změn na jazyk zavedena ve spolupráci s C Technické normy:

  • long long .
  • Další nedílnou typy (tj. pravidla pro volitelné již int typů).
  • UCN změny [N2170 == 07-0030] `` zrušit zákazy týkající se kontroly a základní zdroje všeobecných znaků jména v charakteru a řetězec literály.''
  • zřetězení Narrow / Wide řetězce.
  • Ne Vlas (proměnná délka Pole, díkybohu pro malé milosrdenství).

Některá rozšíření o předzpracování pravidel byly přidány:

  • __func__ makro expanduje do názvu lexikálně aktuální funkci
  • __STDC_HOSTED__
  • _Pragma: _Pragma (X) se rozšiřuje na # pragma X
  • vararg makra (přetížení maker s různým počtem argumentů)
     
    	  # Define zprávu (test, ...) ((test) puts (# test):? Printf (__ VA_ARGS_ _)) 
     
  • prázdné makro argumenty

Mnoho standardních knihovních zařízení se dědí z C99 (v podstatě všechny změny C99 ​​knihovny od svého předchůdce C89):

Viz:


Rozšířené celočíselné typy

Existuje soubor pravidel, jak rozšířený (přesnost) integer typ by se měl chovat, pokud existuje.

Vidět


Dynamické Inicializace a Destruction s souběžnosti

Je nám líto, neměl jsem čas psát tuto položku. Vidět


thread-local storage (thread_local)

Je nám líto, neměl jsem čas psát tuto položku. Vidět


Unicode znaků

Je nám líto, neměl jsem čas psát tuto položku. Prosím, vraťte se později.

  • ?

Kopírování a rethrowing výjimky

Jak zachytit výjimku a pak znovu vyvolat na jiném vlákně? Použití trochu knihovny magie, jak je popsáno v normě 18.8.5 výjimka propagaci:

  • exception_ptr current_exception (); Vrací: exception_ptr objekt, který odkazuje na aktuálně zpracovávaných výjimkou (15,3), nebo kopii aktuálně zpracovávaných výjimkou, nebo null exception_ptr věci, jestliže žádná výjimka je manipulováno. Odkazuje objekt musí zůstat v platnosti nejméně tak dlouho, dokud je exception_ptr objekt, který se odkazuje na to ....
  • void rethrow_exception (exception_ptr p);
  • šablona <class E> exception_ptr copy_exception (E e); Účinky: jako kdyby
     
    	  try { 
    		  házet e; 
    	  } Catch (...) { 
    		  návrat current_exception (); 
    	  } 
    
    To je zvláště užitečná pro přenos výjimku z jednoho závitu na druhou

Externí šablony

Šablona zaměření může být výslovně uvedeno jako způsob, jak potlačit více konkretizace Například.:

 
	  # Include "MyVector.h" 

	  extern template class myVector <int>; // Vypne implicitní konkretizaci níže - 
					  // MyVector <int> bude explicitně instance jinde 

	  void foo (myVector <int> & v) 
	  { 
		  // Použití vektor zde 
	  } 

`` Jinde'' by mohl vypadat nějak takto:

 
	  # Include "MyVector.h" 

	  šablony třídy myVector <int>; // Udělej myVector   klientům k dispozici (např. z sdílené knihovny 

To je v podstatě způsob, jak předcházet výrazným nadbytečné práce překladač a linker.

Vidět


Inline namespace

Inline namespace mechanismus je určena na podporu knihovny vývoj tím, že poskytuje mechanismus, že podpora formu verzí Zvažte.:

 
	  // Soubor V99.h: 
	  inline namespace V99 { 
		  void f (int); // dělá něco lepší než V98 verze 
		  void f (double); // nová funkce 
		  // ... 
	  } 

	  // Soubor V98.h: 
	  namespace V98 { 
		  void f (int); // dělá něco 
		  // ... 
	  } 

	  // Soubor Mine.h: 
	  namespace Mine { 
	  # Include "V99.h" 
	  # Include "V98.h" 
	  } 

Máme zde mají jmenného prostoru Dolu jak s posledním vydáním (V99) a předchozí (V98). Pokud chcete být konkrétní, můžete:

 
	  # Include "Mine.h" 
	  using namespace Mine; 
	  // ... 
	  V98 :: f (1); // starou verzi 
	  V99 :: f (1); // nová verze 
	  f (1); // default version 

Jde o to, že inline specifikátor činí prohlášení z vnořené názvů objeví přesně tak, jak kdyby byla prohlášena v ohradní názvů.

To je velmi `` statické'' a realizátor orientované zařízení v tom, že inline specifikátor musí být umístěna projektant jmenného prostoru - což volbu pro všechny uživatele. To není možné pro uživatele Dolu říci `` Chci výchozí být V98 spíše než V99.

Vidět

  • Standardní 7.3.1 obor názvů definice [7] - [9].

Explicitní konverze subjekty

C + 98 poskytuje implicitní a explicitní konstruktory, že je, převod definován konstruktor prohlásil explicitní mohou být použity pouze v případě explicitních konverzí zatímco jiné konstruktory mohou být použity v případě implicitních konverzí také Například:.

	 struct S {S (int);} // "obyčejný konstruktor" definuje implicitní převod
	 S s1 (1); // ok
	 S s2 = 1, // ​​ok
	 void f (S);
	 f (1); // ok (ale to je často nepříjemným překvapením - co když S je vektor?)

	 struct {E explicitní E (int);} // explicitní konstruktor
	 E e1 (1); // ok
	 E e2 = 1; // error (ale to je často překvapení)
	 void f (E);
	 f (1); // error (chrání proti překvapení - např. std :: vector konstruktor z int, je explicitní)

Nicméně, konstruktor není jen mechanismus pro definování konverzi. Pokud nemůžeme měnit třídu, můžeme definovat konverzní operátor z jiné třídy Například.:

	 struct S {S (int) {} / * ...  * /};

	 struct {SS
		 int m;
		 SS (int x): m (x) {}
		 Provozovatel S () {return S (m);} //, protože S nemají S (SS); non-dotěrný
	  };

	 SS SS (1);
	 S s1 = ss; // ok, jako implicitní konstruktor
	 S s2 (ss); // ok, jako implicitní konstruktor
	 void f (S);
	 f (ss); // ok, jako implicitní konstruktor

Bohužel, neexistuje žádný explicitní převod operátory (protože tam je daleko méně problematické příklady). C + 11 se zabývá tímto dohledu, neboť umožňují převod provozovatelé musí být explicitní Například.:

	 struct S {S (int) {}};

	 struct {SS
		 int m;
		 SS (int x): m (x) {}
		 výslovné operátor S () {návrat S (m);} //, protože S nemají S (SS)
	  };

	 SS SS (1);
	 S s1 = ss; // chyba, jako explicitní konstruktor
	 S s2 (ss); // ok, jako explicitní konstruktor
	 void f (S); 
	 f (ss); // chyba, jako explicitní konstruktor

Viz brát:


Algoritmy zlepšení

Standardní knihovny algoritmy jsou lepší částečně jednoduchým přidáním nových algoritmů, částečně zlepšenými implementací umožněny novými rysy jazyka, a částečně nových jazykových funkcí umožňuje snadnější použití:

  • Nové algoritmy:
     
    	  bool all_of (Iter první, Iter poslední, Pred pred); 
    	  bool any_of (Iter první, Iter poslední, Pred pred); 
    	  bool none_of (Iter první, Iter poslední, Pred pred); 
    
    	  Iter find_if_not (Iter první, Iter poslední, Pred pred); 
    
    	  OutIter copy_if (InIter první, InIter poslední, OutIter výsledek, Pred pred); 
    	  OutIter copy_n (InIter první, InIter :: difference_type n, OutIter výsledek); 
    
    	  OutIter tah (InIter první, InIter poslední, OutIter výsledek); 
    	  OutIter move_backward (InIter první, InIter poslední, OutIter výsledek); 
    
    	  Pár <OutIter1, OutIter2> partition_copy (InIter první, InIter poslední, pred pred OutIter1 out_true, OutIter2 out_false,); 
    	  Iter partition_point (Iter první, Iter poslední, Pred pred); 
    
    	  RAIter partial_sort_copy (InIter první, InIter poslední, RAIter result_first, RAIter result_last); 
    	  RAIter partial_sort_copy (InIter první, InIter poslední, RAIter result_first, RAIter result_last, srovnání comp); 
    	  bool is_sorted (Iter první, Iter poslední); 
    	  bool is_sorted (Iter první, Iter poslední, srovnání comp); 
    	  Iter is_sorted_until (Iter první, Iter poslední); 
    	  Iter is_sorted_until (Iter první, Iter poslední, srovnání comp); 
    
    	  bool is_heap (Iter první, Iter poslední); 
    	  bool is_heap (Iter první, Iter poslední, srovnání comp); 
    	  Iter is_heap_until (Iter první, Iter poslední); 
    	  Iter is_heap_until (Iter první, Iter poslední, srovnání comp); 
    
    	  T min (initializer_list <T> t); 
    	  T min (initializer_list <T> t, srovnání comp); 
    	  T max (initializer_list <T> t); 
    	  T max (initializer_list <T> t, srovnání comp); 
    	  Pár <const T&, const T&> minmax (const T &, const T & b); 
    	  Pár <const T&, const T&> minmax (const T &, const T & b, srovnání comp); 
    	  Pár <const T&, const T&> minmax (initializer_list <T> t); 
    	  Pár <const T&, const T&> minmax (initializer_list <T> t, srovnání comp); 
    	  Pár <Iter, Iter> minmax_element (Iter první, Iter poslední); 
    	  Pár <Iter, Iter> minmax_element (Iter první, Iter poslední, srovnání comp); 
    
    	  void iota (Iter první, Iter poslední, T value); // Pro každý prvek na kterou odkazuje iterátor i v rozsahu [first, last), přiřadí * i = hodnota a zvýší hodnotu jako by + + hodnota 
    
  • Účinky pohybu: Přesun může být mnohem účinnější než kopírování (viz Přesouvání sémantiku Například, pohyb založený na std :: sort () a std :: set :: insert () byla měřena na 15 krát rychlejší než kopírování založené. verze. To je méně působivý než to zní, protože takové standardní knihovny operace pro standardní knihovny typů, jako jsou řetězce a směr, jsou obvykle ručně optimalizované, aby získali vliv pohybu technikami, jako je výměna kopie s optimalizovaným swapů. Nicméně, pokud vaše typ má pohybovou operaci, získáte výhody výkonu automaticky ze standardních algoritmů.

    Zvažte také, že použití pohybů umožňuje jednoduchou a efektivní druh (a další algoritmy) nádob s `` inteligentní'' ukazateli, zejména unique_ptr :

     
    	  šablona <class P> struct Cmp <P> {// tyto * P hodnoty 
    		  bool operator () (P, P b) const {return * <* b;} 
    	  } 
    
    	  vector <std :: unique_ptr <big>> vb; 
    	  // Vyplnit VB s unique_ptr je pro velké objekty 
    
    	  sort (vb.begin (), vb.end (), Cmp <big> ()); // Nezkoušejte to s auto_ptr 
    
  • Use of lambdami: Pro děti, lidé si stěžovali, že musí psát funkce nebo (lépe) funkce předměty určené k použití jako operace, jako CMP <T> výše, pro standardní knihovny (a další) algoritmů. Toto bylo obzvláště bolestivé dělat, když jste napsal velké funkcí (ne), protože v C + 98 jste nemohli definovat lokální funkce objekt použít jako argument, nyní je to možné . Nicméně, lambd nám umožňuje definovat operace `` inline:''
     
    	  sort (vb.begin (), vb.end (), [] (unique_ptr <big>, unique_ptr <big> b) {return * <* b;}); 
     
    Očekávám, že lambd být trochu nadužíváno zpočátku (jako všechny mocné mechanismy).
  • Použití inicializátor seznamů: Někdy, inicializátor seznamy hodit jako argumenty. Například, za předpokladu, že řetězcové proměnné a Nocase bytí case-insensitive porovnání:
     
    	  auto x = max ({x, y, z}, Nocase ()); 
     

Viz brát:


Kontejnerové zlepšení

Vzhledem k tomu, že nové vlastnosti jazyka ao desetiletí stojí za zkušenosti, co se stalo s standardních kontejnerů? Za prvé, samozřejmě jsme dostali několik nových: array (pevné velikosti kontejneru), forward_list (singly-linked list), a neuspořádaných kontejnery (hašovací tabulky). Další, nové funkce, jako je inicializátor seznamy , rvalue odkazy , variadic šablony , a constexpr byly dány k použití. Zvažte std :: vector.

  • Inicializátor seznamy: Nejviditelnější změnou je použití initializer-seznam konstruktorů, aby kontejner, aby se inicializátor seznam jako svůj argument:
     
    	  vector <string> vs = {"Hello", ",", "World!", "\\\\n"}; 
    	  pro (auto s: vs) cout << s; 
     
  • Přesun Provozovatelé: Kontejnery mají nyní pohybovat konstruktory a pohybujte přiřazení (kromě tradičních operacích kopírování). Nejdůležitější důsledkem je to, že se může efektivně vrátit kontejner z funkce:
     
    	  vector <int> make_random (int n) 
    	  { 
    		  vektor <int> ref (n); 
    		  pro (auto & x: ref) x = rand_int (0255); // nějaký generátor náhodných čísel 
    		  návrat ref; 
    	  } 
    
    	  vector <int> v = make_random (10000); 
    	  pro (auto x: make_random (1000000)) cout << x << "\\\\n '; 
     
    Pointou je, že žádné vektory jsou zkopírovány. Přepište to vrátit free-store-přidělených vektoru a budete muset vypořádat s správy paměti. Přepsat toto předat vektor vyplní jako argument make_random () a máte mnohem méně zřejmou kód (plus přidané příležitost pro výrobu chybu).
  • Vylepšené protlačovací operace: Můj oblíbený kontejner provoz push_back (), která umožňuje kontejner růst elegantně:
     
    	  vector <pair <string,int>> vp; 
    	  string s; 
    	  int i; 
    	  while (cin >> s >> i) vp.push_back ({y, i}); 
     
    To bude postavit pár <string,int> z S a I a přesunout ji do VP. Poznámka `` tah'' není `` kopie;'' je push_back verze, která trvá rvalue referenční argument, takže můžeme využít řetězce 's move constructor. Poznámka také použití jednotného inicializátor syntaxi , aby se zabránilo podrobnost.
  • Umístit operace: push_back () pomocí přesunutí konstruktor je mnohem efektivnější v důležitých případech, než tradiční copy-založený, ale v extrémních případech můžeme jít ještě dále. Proč kopírovat / přesouvat něco? Proč si místo ve vektoru a pak budovat požadovanou hodnotu v tomto prostoru? Operace, které dělají, že se nazývají `` umístit'' (význam `` zavedení''). Například emplace_back ():
     
    	  vector <pair <string,int>> vp; 
    	  string s; 
    	  int i; 
    	  zatímco (cin >> s >> i) vp.emplace_back (s, i); 
     
    Umístit trvá variadic šablony argument, a používá to, aby postavit objekt požadovaného typu. Zda emplace_back () je opravdu účinnější než push_back () závisí na zúčastněných typů a provedení (z knihovny a variadic šablon). Pokud si myslíte, že je to důležité, měření. V opačném případě, zvolit na základě estetiky: vp.push_back ({S, I}), nebo vp.emplace_back (s, i);. Pro tuto chvíli, já dávám přednost push_back () verzi, ale to by mohlo v průběhu času měnit.
  • Rozsahem alokátory : Kontejnery mohou nyní držet "skutečné přidělení objekty (se stavem)" a používat tyto k ovládání vnořené / rozsahem rozdělení (např. přidělení prvků v kontejneru)

Je zřejmé, že kontejnery nejsou pouze části standardní knihovny, které užitek z nového jazyka features.Consider:

  • V době kompilace hodnocení: constexpr se používá k zajištění kompilátoru čas hodnocení, , Bitset, doba trvání, char_traits, pole , atomové typy, náhodná čísla, komplexní ., Atd. V některých případech, to znamená lepší výkon, v jiných (tam, kde není alternativa k sestavování čase hodnocení), znamená to, že nepřítomnost chaotický low-level kód a makra.
  • Tice: N-tice by nebylo možné bez variadic šablon

Rozsahem alokátory

Pro kompaktnosti kontejnerových objektů a pro jednoduchost, C + 98 nevyžadoval kontejnery na podporu rozdělovač se státem: přídělce objekty nemusí být uloženy v kontejneru objektů. To je stále výchozí v C + 11, ale je možné použít alokátor s stavu, říkají o alokátor, který drží ukazatel do arény, ze které chcete přidělit Například.:

 
	  template <class T> class Simple_alloc {// C + 98 Styl 
		  // Žádné údaje 
		  // Obvyklé rozdělovač věci 
	  }; 

	  class Arena { 
		  void * p; 
		  int y; 
	  public: 
		  Arena (void * pp, int ss); 
		  // Přidělit od p [0 .. ss-1] 
	  }; 

	  šablona <class T> struct {My_alloc 
		  Arena &; 
		  My_alloc (Arena & aa): (aa) {} 
		  // Obvyklé rozdělovač věci 
	  }; 

	  Arena my_arena1 (new char [100000], 100000); 
	  Arena my_arena2 (new char [1000000], 1000000); 

	  vector <int> v0; // rozdělit pomocí výchozího alokátor 

	  vektor <int, My_alloc <int>> v1 (My_alloc <int> {my_arena1}); // přiřadit z my_arena1 

	  vektor <int, My_alloc <int>> v2 (My_alloc <int> {my_arena2}); // přiřadit z my_arena2 

	  vektor <int, Simple_alloc <int>> v3; // rozdělit pomocí Simple_alloc 

Je typické, že upovídanost být zmírněn tím, použitím typedefs.

Není zaručeno, že výchozí alokátor a Simple_alloc nezabírá místo v vektorového objektu, ale trochu elegantní šablony metaprogramming v knihovně provádění může zajistit, že. Takže, pomocí alokátoru typu ukládá prostor nad hlavou, pouze pokud její předmět má ve skutečnosti stát (jako My_alloc).

Spíše záludný problém může dojít, pokud používáte nádoby a uživatelsky definované rozdělovač: Pokud element ve stejném přidělování prostoru jako jeho kontejneru? Například, pokud používáte Your_allocator pro Your_string přidělit její prvky a já používám My_allocator přidělit prvky My_vector pak, které rozdělovač by měly být použity pro smyčcové prvků v <Your_allocator> My_vector>? Řešení je schopnost říci kontejner, který alokátor předat prvky. Například, za předpokladu, že mám alokátoru My_alloc a chci vektorové <String>, který používá My_alloc jak pro vektorové prvku a příděly řetězec prvků. Za prvé, musím udělat verzi řetězce, který přijímá My_alloc objekty:

 
	  pomocí xstring = basic_string <char, char_traits <znak>, My_alloc <znak>>, // string s mým alokátoru 
 

Pak musím udělat verzi vektoru, který přijímá tyto řetězce, přijímá My_alloc objekt, a předá tento objekt na řetězec:

 
	  pomocí Švec = vector <xstring, scoped_allocator_adaptor <My_alloc <xstring> >>; 	
 

Nakonec, můžeme udělat přídělci <xstring> typu My_alloc:

 
	  Švec v (svec :: allocator_type (My_alloc <xstring> {my_arena1})); 
 

Nyní Švec je vektor řetězců pomocí My_alloc přidělit paměť pro řetězce. Co je nového, je, že standardní knihovna `` adaptér'' ('' wrapper typ'') scoped_allocator_adaptor se používá k označení, že řetězec by měl také používat My_alloc. Všimněte si, že adaptér může (triviálně) převést My_alloc <xstring> na My_alloc <znak> že xstring potřeb.

Takže, máme čtyři alternativy:

 
	  // Vektor a string používat vlastní (výchozí) alokátor: 
	  pomocí svec0 = vector <string>; 
	  svec0 v0; 

	  // Vektor (pouze) používá My_alloc a řetězec používá vlastní (výchozí) alokátor: 
	  pomocí svec1 = vektor <string, My_alloc <string>>; 
	  svec1 v1 (My_alloc <string> {my_arena1}); 

	  // Vektor a string použití My_alloc (jako výše): 
	  pomocí xstring = basic_string <char, char_traits <znak>, My_alloc <znak>>; 
    	  pomocí svec2 = vector <xstring, scoped_allocator_adaptor <My_alloc <xstring> >>; 
    	  svec2 v2 (scoped_allocator_adaptor <My_alloc <xstring>> {my_arena1}); 

	  // Vektor používá My_alloc a řetězec používá My_string_alloc: 
	  pomocí xstring2 = basic_string <char, char_traits <znak>, My_string_alloc <znak>>; 
	  pomocí svec3 = vector <xstring2, scoped_allocator_adaptor <My_alloc <xstring>, My_string_alloc <znak> >>; 	
	  svec3 v3 (scoped_allocator_adaptor <My_alloc <xstring2>, My_string_alloc <znak>> {my_arena1, my_string_arena});  
 

Je zřejmé, že první varianta, svec0 být zdaleka nejvíce obyčejný, ale pro systémy s vážnými souvisejících s pamětí omezení výkonu, mohou ostatní verze (zejména svec2) být důležitý. Několik typedefs by ten kód trochu čitelnější, ale to je dobře, že není něco, co musíte psát každý den. Scoped_allocator_adaptor2 je varianta scoped_allocator_adaptor pro případ, kdy se dva non-default rozdělovač liší.

Viz brát:


std :: array

Standardní kontejner pole je pevné velikosti random-access posloupnost prvků definovaných v <array>. To má žádný prostor režijní náklady nad rámec toho, co je potřeba držet prvky, nepoužívá volný obchod, to může být inicializován s inicializátor seznamu, zná jeho velikost (počet prvků), a nepřevede na ukazatel, pokud výslovně požádá ji, aby. Jinými slovy, je to velmi podobně jako vestavěné pole bez problémů.

 
	  pole <int,6> = {1, 2, 3}; 
	  [3] = 4; 
	  int x = [5]; // x na 0, protože výchozí prvky jsou nulové inicializovány 
	  int * p1 =; // error: std :: array není implicitně převést na ukazatel 
	  int * p2 = a.data (); // ok: dostat ukazatel na první prvek 

Všimněte si, že můžete mít nulové délky pole s, ale to nemůžete odvodit délku pole z inicializátor seznamu:

 
	  array <int> a3 = {1, 2, 3}, // error: unknown Velikost / chybí 
	  array <int,0> a0; // ok: žádné prvky 
	  int * p = a0.data (); // nespecifikováno, nesnažte se ji 

Standardní pole je funkce dělá to atraktivní pro vestavěných systémů programování (a podobné omezeny, výkon kritický, nebo bezpečnostní kritické úkoly). Je sekvenční kontejner tak, že poskytuje obvyklé typy prvků a funkce (stejně jako vektoru):

 
	  šablona <class C> C :: value_type součet (const C &) 
	  { 
		  návrat akumulovat (a.begin (), a.end (), 0); 
	  } 

	  array <int,10> a10; 
	  array <double,1000> A1000; 
	  vector <int> v; 
	  // ... 
	  int x1 = sum (a10); 
	  double x2 = sum (A1000); 
	  int x3 = součet (v); 

 

Také nemusíte dostat (potenciálně ošklivé) odvozeny základní konverze:

 
	  struct Apple: Ovoce {/ * ...  * /}; 
	  struct Hruška: Ovoce {/ * ...  * /}; 

	  void nasty (array <Fruit*,10> & f) 
	  { 
		  f [7] = new Pear (); 
	  }; 

	  jablka pole <Apple*,10>; 
	  // ... 
	  nasty (jablka); // chyba: nelze převést pole <Apple*,10> na pole <Fruit*,10>; 

Pokud to bylo dovoleno, by jablka nyní obsahují hruška.

Viz brát:

  • Standardní: 23.3.1 Třída šablona pole

std :: forward_list

Standardní kontejner forward_list, definované v <forward_list>, je v podstatě singly-provázaný seznam. Podporuje vpřed iterace (pouze) a zaručuje, že prvky se nepohybují, pokud vložíte nebo vymazat jeden. To zabírá minimální prostor (prázdný seznam je pravděpodobně jedno slovo), a neposkytuje size () operace (tak, že nemá k ukládání velkého člena):

 
	  template <ValueType T, Přídělce Alloc = rozdělovač <T>> 
		  vyžaduje NothrowDestructible <T> 
	  class forward_list { 
	  public: 
		  // Obvyklé kontejner věci 
		  // No size () 
		  // No vzad iterace 
		  // No back () nebo push_back () 
	  }; 

Viz brát:

  • Standardní: 23.3.3 Třída šablona forward_list

Neuspořádané kontejnery

Neuspořádané kontejner je druh hash tabulky. C + 11 nabízí čtyři standardní volby:

  • unordered_map
  • unordered_set
  • unordered_multimap
  • unordered_multiset

Měly by být nazýván hash_map atd., ale je jich tolik nekompatibilní používání těchto názvů, které výbor musel zvolit nové názvy a unordered_map, atd. byly nejméně špatné bychom mohli najít. "Neuspořádané" se odkazuje na jeden z klíčových rozdílů mezi mapou a unordered_map: Když iteraci mapě můžete tak učinit v pořadí, pokud by jeho méně než operátor porovnání (ve výchozím nastavení <), zatímco hodnota typu unordered_map není nutné mít méně než porovnávací operátor a hash tabulku není přirozeně poskytují objednávku. Naopak, je prvek typu mapě nemusí mít hash funkci.

Základní myšlenkou je jednoduše používat unordered_map jako optimální verze mapě, kde optimalizace je možné a rozumné. Například:

 
	  Mapa <string,int> m { 
		  {"Dijkstra", 1972}, {"Scott", 1976}, {"Wilkes", 1967}, {"Hammingova", 1968} 
	  }; 
	  m ["Ritchie"] = 1983; 
	  pro (auto x: m) cout << "{" << x.first << "," << x.second << '}'; 

	  unordered_map <string,int> um { 
		  {"Dijkstra", 1972}, {"Scott", 1976}, {"Wilkes", 1967}, {"Hammingova", 1968} 
	  }; 
	  um ["Ritchie"] = 1983; 
	  pro (auto x: um) cout << "{" << x.first << "," << x.second << '}'; 
 

Iterátor nad m. představí prvky v abecedním pořadí, iterace přes um nebude (kromě přes podivné nehodě). Lookup je prováděno velmi odlišně pro ma UM. Pro m vyhledávání zahrnuje log2 (m.size ()) méně než srovnání vzhledem k tomu, um vyhledávání zahrnuje jeden volání funkce hash a jednoho nebo více rovnosti operací. Za pár elementů (řekněme několik desítek), je těžké říct, který je rychlejší. Při větším počtu prvků (např. v tisících), vyhledávání v unordered_map může být mnohem rychlejší než u mapě.

Více přijít.

Viz brát:

  • Standardní: 23,5 Nečíslovaný asociativních kontejnerů.

std :: n-tice

Standardní knihovna tici (N-násobná) je uložena sekvence N hodnot, kde N je konstanta od 0 do značné implementaci definovanou hodnotu, která je definována v <tuple>. Můžete myslet na n-tice jako nejmenovaný struct s členy uvedených typů n-tic prvků. Zejména, je prvky n-tici uloženy kompaktně, n-tice není spojena struktura.

Prvek typy entice mohou výslovně uvedeno jinak odvodit (pomocí make_tuple ()) a prvky mohou být přístup (od nuly) indexu pomocí get ():

 
	  tici <string,int> t2 ("Kylling", 123); 

	  auto t = make_tuple (řetězec ("Sleď"), 10, 1.23), // t, bude mít <string,int,double> typu enticového 
	  string s = dostat <0> (t); 
	  int x = dostat <1> (t); 
	  dvojitý d = dostat <2> (t); 
 

N-tice jsou používány (přímo nebo nepřímo), kdykoli chceme heterogenní seznam prvků v době překladu, ale nechcete definovat pojmenovanou třídu držet. Například, je n-tice používá interně v std :: funkce a std :: bind držet argumenty.

Nejčastěji užitečné tice je 2-násobná, to znamená, že dvojice. Nicméně, je pár přímo podporovány ve standardní knihovně prostřednictvím std :: pair (20.3.3 dvojice). Dvojice může být použit pro inicializaci tici, ale naopak, není tomu tak.

Na porovnávací operátory (==,! =, <, <=,>, A> =) jsou definovány pro n-tic srovnatelných typů prvků.

Viz brát:

  • Standardní: 20.5.2 Třída šablona tice
  • Variadic šablony papír
  • Zesílení :: n-tice

metaprogramming a zadejte znaky

Promiňte. Přijďte později.


std :: funkce a std :: bind

Vázané a funkce standardní funkční objekty jsou definovány v <functional> (spolu s mnoha dalšími funkčními objekty), které slouží k manipulaci funkcí a funkcí argumenty bind se používá, aby se funkce (nebo funkce nebo cokoliv můžete. vyvolat pomocí (...) syntax) a vytvořit funkční objekt s jedním nebo více argumentů argument funkce `` vázán'' nebo pořadí Například.:

 
	  int f (int, char, double); 
	  auto ff = bind (f, _1, 'c', 1,2); // odvodit návratový typ 
	  int x = ff (7); // f (7, 'c', 1,2); 

Toto vázání argumentů se obvykle nazývá `` mazání.'' _1 Je místo-držitel objekt ukazuje, kde první argument ff je jít, když je f nazývá přes ff. První argument se nazývá _1, _2 druhé, a tak dále. Například:

 
	  int f (int, char, double); 
	  auto frev = bind (f, _3, _2, _1); // vzad Argument, aby 
	  int x = frev (1,2, 'c', 7); // f (7, 'c', 1,2); 

Všimněte si, jak auto zachrání nás z nutnosti určit, o jaký typ výsledku vázat.

Je to možné jen svázat argumenty pro přetížené funkce, musíme explicitně uvést, která verze přetížené funkce chceme svázat:

 
	  int g (int); 
	  double g (double); // g () je přetížen 

	  auto g1 = bind (g, _1); // error: které g ()? 
	  auto g2 = bind ((double (*) (double)) g, _1); // ok (ale ošklivý) 

bind () je dodáván ve dvou variantách: jedna je uvedeno výše, a "dědictví" verze, kde se explicitně určit typ vrácení:

 
	  auto f2 = bind <int> (f, 7, 'c', _1); // explicitní návratový typ 
	  int x = f2 (1,2); // f (7, 'c', 1,2); 

Tato druhá verze je nezbytná a je široce používán becasue první (a pro uživatele nejjednodušší) verze nemůže být provedena v jazyce C + 98.

Funkce je typ, který může mít hodnotu jen o něco můžete vyvolat pomocí (...) syntaxe. Zejména může výsledek vázat být přiřazen funkci. Funkce je velmi jednoduchý na používání. Například:

 
	  Funkce <float (int x, int y)> f; // aby objekt funkce 

	  struct int_div {// vzít něco, co můžete volat pomocí () 
		  float operator () (int x, int y) const {return ((float) x) / y;}; 
	  }; 

	  f = int_div (); // přiřadit 
	  cout << f (5, 3) << endl; // volání přes funkční objekt 
	  std :: akumulovat (b, e, 1, f), // prochází krásně 

Členské funkce mohou být považovány za volných funkcí s extra argumentem

 
	  struct {X 
		  int foo (int); 
	  }; 

	  Funkce <int (X*, int)> f; 
	  f = & X :: foo; // ukazatel na člen 

	  X x; 
	  int v = f (x &, 5); // volání X :: foo () pro x s 5 
	  Funkce <int (int)> ff = std :: bind (f, a x, _1); // první argument pro f je a x 
	  v = ff (5) // volání x.foo (5) 

Funkce s jsou užitečné pro zpětná volání, pro průchod operace jako argument, atd. To může být viděno jako náhrada za C + 98 standardních knihoven mem_fun_t funkční objekty, pointer_to_unary_function, atd. Stejně tak bind () může být viděn jako náhrada za bind1 () a bind2 ().

Viz brát:


unique_ptr

  • Unique_ptr (definované v <memory>) poskytuje sémantiku přísné vlastnictví.
    • vlastní objekt je držitelem ukazatel
    • není CopyConstructible, ani CopyAssignable, ale je to MoveConstructible a MoveAssignable.
    • obchody ukazatel na objekt a odstraní tento objekt pomocí přidružené Deleter, když je sám zničen (např. při opuštění bloku rozsah (6,7)).
  • K použití unique_ptr patří
    • poskytování výjimek bezpečnosti pro dynamicky alokované paměti,
    • Předávání vlastnictví dynamicky alokované paměti na funkci,
    • vrácení dynamicky alokovanou paměť z funkce.
    • ukládání ukazatele v nádobách
  • "Co auto_ptr mělo být" (ale to jsme nemohli psát v jazyce C + 98)

unique_ptr spoléhá kriticky rvalue odkazy a přesunout sémantiky.

Zde je konvenční kus výjimky nebezpečného kódu:

 
	  X * f () 
	  { 
		  X * p = new X; 
		  // Něco - možná vyvolá výjimku 
		  návrat p; 
	  } 

Řešením je držet ukazatel na objekt o volném obchodu v unique_ptr:

 
	  X * f () 
	  { 
		  unique_ptr <X> p (nový X); // nebo {new X}, ale ne = new X 
		  // Něco - možná vyvolá výjimku 
		  návrat p.release (); 
	  } 

Teď, když je vyvolána výjimka, bude unique_ptr (implicitně) zničit objekt ukázal. To je základní RAII . Nicméně, pokud opravdu musíme vrátit vestavěný ukazatel, můžeme udělat ještě lépe vrácením unique_ptr:

 
	  unique_ptr <X> f () 
	  { 
		  unique_ptr <X> p (nový X); // nebo {new X}, ale ne = new X 
		  // Něco - možná vyvolá výjimku 
		  návrat p; // přechází vlastnictví z f () 
	  } 

Můžeme použít tuto f takto:

 
	  void g () 
	  { 
		  unique_ptr <X> q = f (); // tah pomocí pohybu konstruktor 
		  q-> memfct (2); // použití q 
		  X x = * q; // copy objektu poukázal na 
		  // ... 
	  } // Q a objekt, který vlastní je zničena na výstupu 

Unique_ptr má "move sémantiku", takže inicializace q s rvalue, že je výsledek volání f () jednoduše převádí své vlastnické právo do q.

Jeden z použití unique_ptr je jako ukazatel v kontejneru, kde jsme mohli použít vestavěný ukazatel s výjimkou problémů výjimek bezpečnosti (a zaručit zničení poukázal na prvky):

 
	  vektor <unique_ptr <string>> vs {new string {"Doug"}, nový řetězec {"Adams"}}; 
 

unique_ptr je reprezentován jednoduchým vestavěným ukazatelem a režie pomocí jednoho srovnání s vestavěným ukazatelem jsou nepatrné. Zejména, unique_ptr nenabízí žádnou formu dynamického kontroly.

Viz vzít


shared_ptr

Shared_ptr je používán reprezentovat sdílené vlastnictví, který je, když dva kusy kódu potřebuje přístup k některým údajům, ale nemá ani výhradní vlastnictví (v tom smyslu, že odpovědnost za zničení objektu). Shared_ptr je druh počítá ukazatel, kde se objekt ukázal odstraněn při použití počet klesne na nulu. Zde je velmi umělý příklad:

	 void test ()
	  {
		 shared_ptr <int> p1 (new int); // počet je 1
		  {
			 shared_ptr <int> p2 (p1); // počet je 2
			  {
				 shared_ptr <int> p3 (p1); // počet je 3
			 } // Počet sahá až do 2
		 } // Počet sahá až do 1
	 } // Zde počet jde do 0 a int se zrušuje.

Realističtější příklad by bylo ukazatele na uzly v obecném grafu, kde někdo chtěl odstranit ukazatel na uzel by vědět, jestli někdo držel ukazatel na tento uzel. Pokud uzel může obsahovat zdroje, které vyžadují akci destruktoru (např. soubor zpracovat tak, že soubor musí být uzavřena, pokud je odstraněn uzel). Dalo by se uvažovat o shared_ptr být pro to, co byste měli zvážit zapojení v garbage collector pro, kromě toho, že možná nemáte dostatek odpadky pro to, aby se ekonomické, váš výkon prostředí neumožňuje, nebo podařilo zdrojů není jen . paměti (např. že soubor rukojeť) Například:

	 struct Node {// poznámka: Node může být poukázal na několika dalších uzlů.
		 shared_ptr <Node> vlevo;
		 shared_ptr <Node> pravdu;
		 File_handle f;
		  // ...
	  };

Zde Node "s destructor (implicitně generována destruktor bude stačit) odstraní své sub-uzly; že je, levá a pravá" se odvolává s destruktory. Vzhledem k tomu, vlevo je shared_ptr, Node poukázal na (pokud existuje), se zrušuje, pokud vlevo byl poslední ukazatel na něj, právo zacházet podobně a f 's destruktor dělá, co je třeba pro f.

Všimněte si, že byste neměli používat shared_ptr jen předat ukazatel z jednoho vlastníka na druhého, to je to, co unique_ptr je pro a unique_ptr dělá, že levnější a lepší. Pokud jste používali spočítat ukazatele jako výstupní hodnoty z výroby funkcí a podobně, zvažte upgrade na unique_ptr než shared_ptr.

Prosím, bezmyšlenkovitě nahradit ukazatele s shared_ptr s ve snaze zabránit úniků paměti, shared_ptr s nejsou všelékem, ani jsou oni bez nákladů:

  • kruhová souvisí struktura shared_ptr s způsobí únik paměti (budete potřebovat nějaký logický komplikace prolomit kruh, např. pomocí weak_ptr),
  • "Sdílené vlastnictví objekty" mají tendenci zůstat "live" na dobu delší než scoped objektů (což způsobuje vyšší průměrné využití prostředků),
  • společné ukazatele v multi-threaded prostředí může být drahé (protože je třeba, aby se data závodů na použití počtu),
  • destruktor sdílený objekt neprovede v předvídatelné době, a
  • algoritmy / logické pro aktualizaci všech sdílený objekt je snazší se dostat v pořádku, než na objekt, který není sdílený.

. Shared_ptr představuje sdílenou odpovědnost, ale společná vlastnická není můj ideál: Je lepší, když objekt má určitý vlastníka a určitou, předvídatelný životnost.

Viz vzít

  • C + + ponor: shared_ptr (20.7.13.3)

weak_ptr

Slabé ukazatele jsou často vysvětleny jako to, co budete muset ukončit smyčky v datových strukturách řízených pomocí shared_ptr s.. Myslím, že je lepší myslet na weak_ptr jako ukazatel na něco, co

  1. budete potřebovat přístup k (pouze) v případě, že existuje, a
  2. může mazány (někým jiným), a
  3. musí mít destruktor nazývá po posledním použití (obvykle odstranit anon-operační paměť)

Zvažte implementaci starého "asteroidů hry". Všechny asteroidy jsou ve vlastnictví "ve hře", ale každý asteroidy musí sledovat okolní asteroidy a zpracovat kolize. Kolize obvykle vede k destrukci jednoho nebo více asteroidů. Každý asteroid musí vést seznam dalších asteroidů ve svém sousedství. Všimněte si, že je na takový soused seznamu by neměla vést astroid "živý" (tak shared_ptr by bylo nevhodné). Na druhé straně musí být asteroid být zničena, zatímco druhý asteroid je při pohledu na ni (např. pro výpočet účinek kolize). A samozřejmě, musí destruktor asteroidy být nazýván uvolnit prostředky (například jako spojení s grafickým systémem). To, co potřebujeme, je seznam asteroidů, které by mohly ještě být neporušené a způsob "držení se jedné, pokud existují" na chvíli. Weak_ptr dělá jen to:

	 void majitel ()
	  {
		  // ...
		 vektor <shared_ptr <Asteroid>> va (100);
		 for (int i = 0; i <va.size (); + + i) {
			  // ...  výpočet sousedy pro nové planetky ...
			 va [i] reset (nový Asteroid (weak_ptr <Asteroid> (va [soused])).;
			 spustit (i);
		  }
		  // ...
	  }

reset () je funkce, aby shared_ptr odkazovat na nový objekt.

Samozřejmě, že jsem radikálně zjednodušený "majitel" a dal každému novému asteroidu pouze jeden soused. Klíčem k úspěchu je, že dáváme asteroid a weak_ptr k tomuto sousedovi. Majitel udržuje shared_ptr zastupovat vlastnictví, který je sdílený kdykoli Asteroid hledá (ale jinak). Kolize Výpočet pro Asteroid bude vypadat nějak takto:

	 void kolize (weak_ptr <Asteroid> p)
	  {
		 if (auto q = p.lock ()) {// p.lock vrátí shared_ptr na objekt p je
			  // ...  že Asteroid stále existují: výpočet ...
		  }
		  else {
			  // ...  oops: že Asteroid již bylo zničeno: zapomeňte na to (smazat weak_ptr k němu ...
		  }
	  }

Všimněte si, že i když majitel rozhodne vypnout hru a odstraní všechny asteroidy (tím, že zničí v shared_ptr s představující vlastnictví) každý Asteroid, který je ve středu výpočtu kolizi ještě dokončí správně (protože po p.lock () platí shared_ptr, že se nejen stane neplatným).

Očekávám, že najít weak_ptr používat mnohem vzácnější než "obyčejný" shared_ptr použití, a já doufám, že unique_ptr bude mnohem populárnější než použití shared_ptr protože unique_ptr představuje jednodušší (a účinnější) pojem vlastnictví a (proto) umožňuje lepší místní uvažování.

Viz vzít

  • C + + ponor: weak_ptr (20.7.13.3)

Garbage collection ABI

Garbage collection (automatická recyklace neregistrovanou regionů paměti) je volitelná v C + +, který je, garbage collector není povinnou součástí implementace. Nicméně, C + 11 stanoví definici toho, co GC dělat, když jeden je používán a ABI (Application Binary Interface) ke kontrole jeho činnosti.

Pravidla pro ukazatele a životů jsou vyjádřeny v "bezpečné odvozené ukazatele" (3.7.4.3), zhruba: ". Ukazatel na něco přidělené nové nebo na sub-objekt této smlouvy" Zde jsou některé příklady ", které nejsou bezpečně odvozené ukazovátka" aka "skrytých ukazatelů" aka co nedělat v programu, který chcete považovat za dobře vychovaný a srozumitelné pro běžné smrtelníky:

  • Udělejte ukazatel bod "někde jinde" na chvíli
     
    	  int * p = new int; 
    	  p + = 10; 
    	  // ...  sběratel může spustit zde ... 
    	  p-= 10; 
    	  * P = 10; // můžeme si být jisti, že int je tam ještě?  
    
  • Skrytí ukazatele v int
     
    	  int * p = new int; 
    	  int x = reinterpret_cast <int> (p); // non-přenosný 
    	  p = 0; 
    	  // ...  sběratel může spustit zde ... 
    	  p = reinterpret_cast <int*> (x); 
    	  * P = 10; // můžeme si být jisti, že int je tam ještě? 
    
  • Existuje mnoho dalších a dokonce odpornější triky Myslíš, že I / O, myslím, že "rozptyl bitů kolem v různých slovech", ...

Existují legitimní důvody, aby zakryly ukazatele (např. xor trik výjimečně paměť s omezenými aplikace), ale ne tolik, jak někteří programátoři myslí.

Programátor může určit, kde neexistují žádné ukazatele, které se nacházejí (např. v obraze) a to, co paměť nemohou být reklamovány, i když sběratel nemůže najít ukazatel do něj:

 
	  void declare_reachable (void * p) // oblast paměti začíná na p 
						  // (A přidělené určité alokátoru 
						  // Operace, která si pamatuje jeho velikost) 
						  // Nesmí být shromažďovány 
	  šablona <class T> T * undeclare_reachable (T * p); 

	  void declare_no_pointers (char * p, size_t n); // p [0 .. n] drží žádné ukazatele 
	  void undeclare_no_pointers (char * p, size_t n); 
 

Programátor může zeptat, jaká pravidla pro ukazatele bezpečnosti a rekultivace je v platnosti:

 
	  enum class pointer_safety {uvolněná, přednostní, přísná}; 
	  pointer_safety get_pointer_safety (); 
 

3.7.4.3 [4]: Je-li ukazatel hodnota, která není bezpečně odvozená Hodnota ukazatele je dereferenced nebo uvolnit, a odkazoval se na kompletní objekt je dynamického skladování trvání a dosud nebyla prohlášena za dostupná (20.7.13.7), chování není definován.

  • uvolněná: bezpečně odvozená a nikoliv bezpečně-odvozené ukazatele jsou považovány equivalently, rád C a C + 98, ale to nebyl můj záměr - chtěl jsem, aby GC, pokud uživatel nedržel platný ukazatel kolem objektu.
  • preferovat: jako uvolněný, ale garbage collector může být spuštěn jako detektoru netěsností a / ​​nebo detektoru dereferences o "chybné ukazatele"
  • přísné: bezpečně odvozená a nikoliv bezpečně-odvozené ukazatele může být zacházeno odlišně, tj. garbage collector může být spuštěn a bude ignorovat ukazatele, že to není bezpečné odvozená

Neexistuje žádný standardní způsob, jak říci, která varianta dáváte přednost. Za to, že "kvalita provedení" a "programovací prostředí" problém.

Viz vzít


Paměťový model,

Paměťový model je dohoda mezi stroje architekty a kompilátoru spisovatelů, aby zajistily, že většina programátorů nemusí přemýšlet o podrobnostech moderního počítačového hardwaru. Bez paměťového modelu, jen velmi málo věcí, týkající se závitů, blokování a zámek bez programování smysl.

Klíč je záruka: Dva závity provedení můžete aktualizovat a přístup k samostatné paměťových míst, aniž by zasahoval navzájem. Ale co je `` paměťové místo?'' Místo v paměti buď objekt skalárního typu nebo maximální posloupnost sousedících bit-pole mají všechny nenulové šířky. Například, zde S má přesně čtyři oddělené paměťových míst:

 
	  struct S { 
		  char; // location # 1 
		  int b: 5, // location # 2 
		  int c: 11, 
		  int: 0, // note :: 0 je "zvláštní" 
		  int d: 8; // location # 3 
		  struct {int ee: 8;} e, // location # 4 
	  }; 

Proc JE TO důležité? Proč je to snad jasné? Nebylo to vždy pravda? Problémem je, že když několik výpočty mohou skutečně probíhat současně, že je několik (zřejmě) nesouvisející instrukce mohou provádět současně, mohou vtípky z paměti hardwaru dostat vystaveno. Ve skutečnosti, v případě neexistence kompilátoru podporu, budou problémy výuky a data zřetězením a podrobnosti o mezipaměti použití musí být vystavena v cestách, které jsou zcela nezvládnutelná pro aplikace programátoru. To platí iv případě, že žádné dvě vlákna jsou definovány pro sdílení dat! Zvažte, dva samostatně kompilovaných `` témata:''

 
	  // Závit 1: 
	  char c; 
	  c = 1; 
	  int x = c; 

	  // Závit 2: 
	  char b; 
	  b = 1; 
	  int y = b; 
 

Pro větší realističnosti, mohl jsem použít samostatný kompilaci (v rámci každé vlákno) s cílem zajistit, že kompilátor / optimalizátor by nebyli schopni eliminovat přístupů do paměti a prostě ignorovat C a B a přímo inicializovat x a y s 1. Jaké jsou možné hodnoty x a y? Podle C + 11 jedinou správnou odpovědí je zřejmý: 1 a 1. Důvodem je to zajímavé, je to, že pokud budete mít konvenční dobrý pre-souběžnost C nebo C + + kompilátor, možné odpovědi jsou 0 a 0 (nepravděpodobné), 1 a 0, 0 a 1, a 1 a 1. To bylo pozorováno `` ve volné přírodě.'' Jak na to? Linker může přidělit C a B vpravo vedle sebe (ve stejné slovo) - nic v C nebo C + + 1990s norem říká jinak. V tom, C + + se podobá všechny jazyky, které nejsou konstruovány s reálným současným hardwarem v mysli. Nicméně, většina moderních procesorů nemůže číst nebo psát jeden znak, musí číst nebo psát celé slovo, tak přiřazení c je opravdu `` přečíst toto slovo obsahující c, nahradí část C, a napište slovo znovu. " "Vzhledem k tomu, postoupení b je podobný, existuje spousta příležitostí pro dvě vlákna na hadry navzájem i přesto, že vlákna nejsou (podle jejich zdrojového textu) sdílení dat!

Takže, C + 11 zaručuje, že žádné takové problémy nastanou `` oddělených paměťových míst'' přesněji:. Paměti umístění nemůže být bezpečně přístupné dva závity bez nějaké formy uzamčení, pokud nejsou oba čtou přístupů. Všimněte si, že různé bitfields v rámci jednoho slova nejsou samostatné paměťová místa, takže se nemusíte sdílet structs s ​​bitfields mezi závity bez nějaké formy zamykání. Kromě tohoto upřesněním, C + + paměťový model je prostě ``, jak všichni očekávají.''

Nicméně, to není vždy snadné myslet o low-level souběžnými záležitostmi. Zvažte:

 
	  // Start s x == 0 a y == 0 

	  if (x) y = 1, // závit 1  

	  if (y) x = 1, // Thread 2  
 

Je tu nějaký problém? Přesněji řečeno, je tam dat závod? (Ne, není).

Naštěstí jsme již přizpůsobeny moderní době a každý proud C + + kompilátor (pokud vím) dává jednu správnou odpověď, a tak tomu je už roky. Dělají tak pro většinu (ale bohužel zatím pro všechny) na záludné otázky. Koneckonců, C + + byla použita za závažné systémové programování paralelních systémů `` navždy.'' Standard by měl dále zlepšit věci.

Viz vzít


Threads

Vlákno je charakter popravy / výpočty v programu. V C + 11, stejně jako v hodně moderní výpočetní techniky, může vlákno - a obvykle dělá - sdílení adresního prostoru s ostatními vlákny. V tomto, se liší od postupu, který obvykle není přímo sdílet data s jinými procesy. C + + měli řadu příze implementací pro různé hardware a operační systémy v minulosti, to, co je nového, je standardní knihovna závity knihovna, standardní závit ABI.

Mnoho tlustých knih a desítky tisíc dokumentů se píše o souběžnosti, paralelismus, a řezání závitů, tato položka FAQ lehce kloužou po povrchu. Je těžké uvažovat o souběžnosti. Pokud chcete udělat souběžnou programování, alespoň číst knihu. Nespoléhejte jen na manuální, standardní, nebo FAQ.

Závit je zahájen sestavením std :: vlákno s funkcí nebo funkce objektu (vč. lambda ):

 
	  # Include <thread> 

    	  void f (); 

	  struct {F 
		  void operator () (); 
	  }; 

	  int main () 
	  { 
		  std :: závit t1 {f}; // f () spustí v samostatném vlákně 
		  std :: závit t2 {F ()} // F () () spustí v samostatném vlákně 
	  } 

Bohužel, toto je nepravděpodobné, aby všechny užitečné výsledky - bez ohledu na f () a F () by mohl udělat. Háček je, že program může ukončit před nebo po t1 provede f () a před nebo po t2 provede f (). Musíme počkat na dva úkoly k dokončení:

 
	  int main () 
	  { 
		  std :: závit t1 {f}; // f () spustí v samostatném vlákně 
		  std :: závit t2 {F ()} // F () () spustí v samostatném vlákně 

		  t1.join (); // počkat na t1 
		  t2.join (); // počkat na t2 
	  } 

Join () s zajištěno, že nemáme ukončit, dokud závity byly dokončeny. Chcete-li `` připojit'' znamená `` čekat na vlákno ukončit.''

Obvykle, bychom rádi předali některé argumenty na úkol být provedeny (já říkám něco spuštěn na vlásku úkolu). Například:

 
    	  void f (vector <double> &); 

	  struct {F 
		  vector <double> & v.; 
		  F (vector <double> a vv): v {vv} {} 
		  void operator () (); 
	  }; 

	  int main () 
	  { 
		  std :: závit t1 {std :: bind (f, some_vec)}; // f (some_vec) spustí v samostatném vlákně 
		  std :: závit t2 {F (some_vec)}; // F (some_vec) () spustí v samostatném vlákně 

		  t1.join (); 
		  t2.join (); 
	  } 

V podstatě, standardní knihovna bind je funkční objekt svých argumentů.

Obecně platí, že bychom také chtěli získat výsledek zpět od popraveného úkolu. S hladkým úkolů, není pojem návratové hodnoty, doporučuji std :: budoucnost za to. Alternativní, můžeme předat argument k úkolu říkat to, kam umístit své výsledky: Například:

 
    	  void f (vector <double> &, double * res); // místo výsledkem res 

	  struct {F 
		  vector <double> & v.; 
		  double * res; 
		  F (vector <double> & vv, double * p): v {vv}, res {p} {} 
		  void operator () (); // místo výsledkem res 
	  }; 

	  int main () 
	  { 
		  double res1; 
		  double res2; 

		  std :: závit t1 {std :: bind (f, some_vec, a res1)}; // f (some_vec, a res1) vykonává v samostatné vlákno 
		  std :: závit t2 {F (some_vec, a res2)}; // F (some_vec, a res2) () spustí v samostatném vlákně 

		  t1.join (); 
		  t2.join (); 
		
		  std :: cout << res1 << "" << res2 << "\\\\n '; 
	  } 

Ale co o chybách? Co když úkol vyvolá výjimku? Pokud úloha vyvolá výjimku a nezachycuje sama std :: terminate () se nazývá. To obvykle znamená, že program skončí. My se obvykle snaží spíše obtížné, aby se zabránilo, že. Std :: Budoucnost může přenášet výjimku pro rodiče / volající vlákno, to je jeden z důvodů, proč jsem rád futures. V opačném případě, vrátí nějaký chybový kód.

Když vlákno jde mimo rozsah programu je terminate () d, pokud jeho úkolem bylo dokončeno. To je samozřejmě třeba se vyhnout.

Neexistuje žádný způsob, jak požádat o vlákno ukončit (tj. žádost, že opustíte nejdříve a stejně elegantně jako je to možné), nebo vynutit vlákno ukončit (tj. zabít). Zbývá možnostmi

  • navrhování vlastní družstvo `` přerušení mechanismus'' (s kusem sdílených dat, která volající vlákno lze nastavit na tzv. vlákno pro kontrolu (a rychle a elegantně ukončit, pokud je nastavena to)),
  • `` Děje native'' (pomocí nitě :: native_handle (), abyste získali přístup k operačnímu systému je pojem vlákno),
  • zabít proces (std :: quick_exit ()),
  • zabít program (std :: ukončení ()).

To bylo vše, výbor by mohl dohodnout. Zejména zástupci POSIX byl vehementně proti jakékoliv formě `` vlákna zrušení'' jakkoli C + + 's model zdrojů spoléhat na destruktorů. Neexistuje ideální řešení pro každý systém a každou možnou aplikaci.

Základním problémem se závity jsou datová závody, které se mohou dvě vlákna běží v jednom adresním prostoru nezávisle přístup k objektu, v cestách, které způsobují nedefinované výsledky. Pokud jeden (nebo oba) píše na objekt a další (nebo obojí), čte objekt mají `` závod'' pro který dostane její operace (y) udělal první. Tyto výsledky jsou nejen nedefinované, jsou obvykle zcela nepředvídatelné. V důsledku toho, C + 11 stanoví některá pravidla / záruky pro programátora, aby se zabránilo datové závody:

  • C + + standardní knihovna funkce nesmí přímo ani nepřímo přístup k objektům, které jsou přístupné podle vláken jiných než aktuální vlákno, pokud objekty jsou přístupné přímo nebo nepřímo pomocí funkce argumenty, včetně tohoto.
  • C + + standardní knihovna funkce nesmí přímo ani nepřímo upravit objekty, které jsou přístupné podle vláken jiných než aktuální vlákno, pokud objekty jsou přístupné přímo nebo nepřímo pomocí funkce tyto nonconst argumentů, včetně tohoto.
  • C + + standardní knihovna implementace se musí vyhnout datové závody, kdy se různé prvky ve stejném pořadí, upravena současně.

Souběžný přístup k potoku objektu, stream vyrovnávací paměti objektu, nebo C Library streamu více vláken může vyústit v datovém závodu, pokud není uvedeno jinak. Takže se nemusíte sdílet výstupní proud mezi dvěma závity, pokud jste nějak regulovat přístup k ní.

Můžete

Viz vzít


Vzájemné vyloučení

Mutex je primitivní objekt využití pro řízení přístupu v multi-threaded systému. Nejzákladnější použití je

 
	  std :: mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  m.lock (); 
	  // Manipulovat sdílených dat: 
	  sh + = 1; 
	  m.unlock (); 
 

Pouze jedno vlákno v době lze v oblasti kódu mezi zámkem () a odemknout () (často volal Kritická oblast). Pokud druhý podproces pokusí m.lock (), zatímco první závit je provedení v tomto regionu, je, že druhý podproces blokovány, dokud první provede m.unlock (). To je jednoduché. Co není jednoduché je použít mutexy způsobem, který nezpůsobí vážné problémy: Co když vlákno `` zapomene'' odemknout ()? Co když podproces pokusí zablokovat () stejný mutex dvakrát? Co když podproces čeká velmi dlouho před dělá unlock ()? Co když vlákno je třeba zamknout () dvě mutexy dělat svou práci? Kompletní odpovědi vyplnit knihy. Tady (a v Zámky části ) jsou jen syrové základy.

Kromě lock (), mutextry_lock () operace, které lze použít, aby se pokusili dostat do kritické oblasti bez rizika ucpání:

 
	  std :: mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  if (m.try_lock ()) { 
		  // Manipulovat sdílených dat: 
		  sh + = 1; 
		  m.unlock (); 
	  else { 
		  // Možná dělat něco jiného 
	  } 

Recursive_mutex je mutex, který je možné získat více než jednou nití:

 
	  std :: recursive_mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  void f (int i) 
	  { 
		  // ... 
		  m.lock (); 
		  // Manipulovat sdílených dat: 
		  sh + = 1; 
		  if (- i> 0) f (i); 
		  m.unlock (); 
		  // ... 
	  } 

Tady jsem byl nestydatý a nechte f () volat sebe. Typicky, kód je jemnější. Rekurzivní volání bude nepřímý podél linie f () volá g (), která volá h (), která volá f ().

Co když potřebuji získat mutex během následujících deseti sekund? Třída timed_mutex je nabízen za to. Jeho operace jsou specializované verze try_lock () s přidruženou lhůtě:

 
	  std :: timed_mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  if (m.try_lock_for (std :: chrono :: sekund (10))) { 
		  // Manipulovat sdílených dat: 
		  sh + = 1; 
		  m.unlock (); 
	  } 
	  else { 
		  // Jsme nedostali mutex, dělat něco jiného 
	  } 

Try_lock_for () trvá relativní čas, je trvání jako svůj argument. Pokud místo toho chcete počkat, až k pevnému bodu v čase, time_point můžete použít try_lock_until ():

 
	  std :: timed_mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  if (m.try_lock_until (půlnoc)) { 
		  // Manipulovat sdílených dat: 
		  sh + = 1; 
		  m.unlock (); 
	  } 
	  else { 
		  // Jsme nedostali mutex, dělat něco jiného 
	  } 

Půlnoc je chabý vtip: mechanismus jako nízké úrovni, jako mutexy, časová je více pravděpodobné, že bude milisekund, než hodiny.

K dispozici je samozřejmě také recursive_timed_mutex.

Mutex je považován za zdroj (jak je obvykle používán reprezentovat reálný zdroj) a musí být viditelné alespoň dvěma závity Aby byly užitečné. V důsledku toho, nemůže být zkopírován nebo přesunut (nemůžete jen vytvořit další kopii registru hardwaru vstupní).

To může být překvapivě obtížné dostat zámek () s a odemknutí () S, aby odpovídala. Přemýšlejte o složitých řídicích struktur, chyby a výjimky. Pokud máte na výběr, použijte zámky spravovat mutexy, které vám ušetří i vaši uživatelé hodně spánku.

Viz vzít


Zámky

Zámek je objekt, který může obsahovat odkaz na mutex a může odemknout () na mutex během zámek s zničení (například při odchodu bloku rozsah). Vlákno může použít zámek na pomoc v řízení mutex vlastnictví v výjimek bezpečným způsobem. Jinými slovy, zámek implementuje Resource akvizice je inicializace pro vzájemné vyloučení Například.:

 
	  std :: mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  void f () 
	  { 
		  // ... 
		  std :: unique_lock Seal (m); 
		  // Manipulovat sdílených dat: 
		  sh + = 1; 
	  } 

Zámek může být přesunut (účel zámku je reprezentovat místní vlastnictví non-místní zdroj), ale ne kopírovat (která kopie by vlastní zdroj / mutex?).

Tento jednoduchý obrázek zámku je zahalena unique_lock s prostory dělat téměř vše, co mutex může, ale bezpečnější. Například, můžeme použít zámek k tomu pokusit zámek:

 
	  std :: mutex m; 
	  int sh; // společné údaje 
	  // ... 
	  void f () 
	  { 
		  // ... 
		  std :: unique_lock Seal (m, std :: defer_lock); // aby se zámek, ale ne získat mutex 
		  // ... 
		  if (lck.try_lock ()) { 
			  // Manipulovat sdílených dat: 
			  sh + = 1; 
		  } 
		  else { 
			  // Možná dělat něco jiného 
		  } 
	  } 

Podobně, unique_lock podporuje try_lock_for () a try_lock_until (). Co získáte od používání zámek spíše než mutex přímo je zpracování výjimek a ochrana proti zapomnění odemknout (). V souběžné programování, budeme potřebovat veškerou pomoc, kterou můžeme získat.

Co když budeme potřebovat dva zdroje reprezentované dvěma mutexy? Naivní způsob je získat mutexy v pořadí:

 
	  std :: mutex m1; 
	  std :: mutex m2; 
	  int sh1; // společné údaje 
	  int SH2 
	  // ... 
	  void f () 
	  { 
		  // ... 
		  std :: unique_lock lck1 (m1); 
		  std :: unique_lock lck2 (m2); 
		  // Manipulovat sdílených dat: 
		  sh1 + = sh2; 
	  } 

To má potenciálně smrtelnou chybu, že některé jiné vlákno by se mohl pokusit získat m1 a m2 v opačném pořadí, takže každý měl jeden zámků potřebných postupovat a bude čekat věčně druhý (tomu se říká zablokování). S mnoha zámků v systému, který je skutečné nebezpečí. V důsledku toho, že standardní zámky mají dvě funkce pro (bezpečně) se snaží získat dva nebo více zámky:

 
	  void f () 
	  { 
		  // ... 
		  std :: unique_lock lck1 (m1, std :: defer_lock); // aby zámky, ale zatím se snaží získat mutexy 
		  std :: unique_lock lck2 (m2, std :: defer_lock); 
		  std :: unique_lock lck3 (m3, std :: defer_lock); 
		  zámek (lck1, lck2, lck3); 
		  // Manipulovat sdílených dat 
	  } 

Je zřejmé, že provádění zámku () musí být pečlivě vytvořený, aby se zabránilo zablokování. V podstatě, bude to dělat ekvivalent pečlivému používání try_lock () S. Pokud je zámek () selže získat všechny zámky to bude hodit výjimku. Vlastně, můžete lock () se žádný argument, který se zámkem (), try_lock (), a odemknout () členské funkce (např. mutex), takže nemůžeme být konkrétní, o nichž výjimka lock () může hodit, to závisí na jeho argumenty.

Pokud dáváte přednost použití try_lock () S yourself, je ekvivalentní k zablokování () na pomoc:

 
	  void f () 
	  { 
		  // ... 
		  std :: unique_lock lck1 (m1, std :: defer_lock); // aby zámky, ale zatím se snaží získat mutexy 
		  std :: unique_lock lck2 (m2, std :: defer_lock); 
		  std :: unique_lock lck3 (m3, std :: defer_lock); 
		  int x; 
		  if ((x = try_lock (lck1, lck2, lck3)) == -1) {// Vítejte na C půdy 
			  // Manipulovat sdílených dat 
		  } 
		  else { 
			  // X drží index mutex jsme nemohli získat 
			  // Např. pokud lck2.try_lock () failed x == 1 
		  } 
	  } 

Viz vzít

  • Standardní: 30.4.3 Zámky [thread.lock]
  • ?

Stav proměnné

Stav proměnné poskytují synchronizačních primitiv slouží k zablokování vlákno až oznámeno jiným závitem, že některé podmínka je splněna, nebo dokud systémový čas je dosaženo.

Je nám líto, neměl jsem čas psát tuto položku. Prosim, vraťte sobě později.

Viz vzít

  • Standardní: 30,5 Stav proměnných [thread.condition]
  • ?

Čas utility

My často chtějí čas věci nebo dělat věci, závislé na načasování. Například standardní knihovna mutexy a zámky poskytují možnost pro vlákno čekat na určitou dobu (doba trvání), nebo počkat, až od daného bodu v čase (time_point).

Pokud chcete znát aktuální time_point můžete zavolat now () pro jeden ze tří hodin s: system_clock, monotonic_clock, high_resolution_clock. Například:

 
	  monotonic_clock :: time_point t = monotonic_clock :: now (); 
	  // Něco 
	  monotonic_clock :: doba d = monotonic_clock :: now () - t; 
	  // Něco se d časových jednotek 

Hodiny vrací time_point a trvání je rozdíl dvou time_point s od stejného hodiny. Jako obvykle, pokud nemáte zájem o bližší informace, auto je tvůj přítel:

 
	  auto t = monotonic_clock :: now (); 
	  // Něco 
	  auto d = monotonic_clock :: now () - t; 
	  // Něco se d časových jednotek 

Časové zařízení zde mají účinně podpořit používá hluboko v systému, které neposkytují pohodlí zařízení, které vám pomohou udržet si své sociální kalendáře. Ve skutečnosti, časové zařízení pochází s přísnými požadavky na fyziku vysokých energií. Chcete-li být schopni vyjádřit všechny časové škály (např. celá staletí a pikosekund), vyhnout se zmatku o jednotky, překlepy, a zaokrouhlování chyby, jsou doba s a time_point s vyjádřené pomocí kompilace-time racionální číslo balíčku. Doba trvání má dvě části: Čísla hodiny `` tick'' a něco (`` období''), které říká, že to, co klíště znamená (? Je to druhý nebo milisekunda), období je součástí doby trvání typu. Tato tabulka ze standardní záhlaví <poměr>, může vymezení období soustavy SI (také známý jako MKS nebo metrického systému) vám představu o rozsahu použití:


	// Pohodlí SI typedefs:
	typedef ratio<1, 1000000000000000000000000> yocto;  // podmíněně podporovány
	typedef ratio<1,    1000000000000000000000> zepto;  // podmíněně podporovány
	typedef ratio<1,       1000000000000000000> atto;
	typedef ratio<1,          1000000000000000> femto;
	typedef ratio<1,             1000000000000> pico;
	typedef ratio<1,                1000000000> nano;
	typedef ratio<1,                   1000000> micro;
	typedef ratio<1,                      1000> milli;
	typedef ratio<1,                       100> centi;
	typedef ratio<1,                        10> deci;
	typedef ratio<                       10, 1> deca;
	typedef ratio<                      100, 1> hecto;
	typedef ratio<                     1000, 1> kilo;
	typedef ratio<                  1000000, 1> mega;	
	typedef ratio<               1000000000, 1> giga;
	typedef ratio<            1000000000000, 1> tera;
	typedef ratio<         1000000000000000, 1> peta;
	typedef ratio<      1000000000000000000, 1> exa;	
	typedef ratio<   1000000000000000000000, 1> zetta;  // podmíněně podporovány
	typedef ratio<1000000000000000000000000, 1> yotta;  // podmíněně podporovány

V době kompilace racionální čísla poskytují obvyklý aritmetiku (+, -, * a /) a porovnání (==, =, <, <=,>,> =!) Operátoři pro jakékoli kombinace trvání sa time_point s smysl ( např. nemůžete přidat dva time_points). Tyto operace jsou také kontrolovány na přetečení a dělit nulou. Vzhledem k tomu, je v době kompilace zařízení, nebojte se o run-time výkon. Kromě toho můžete použít + +, -, + =, - =, * =, a / = o trvání s a TP + = d a tp-= d pro time_point TP a trvání d..

Zde jsou některé příklady hodnot pomocí standardní doba typů, jak jsou definovány v <chrono>:

 
	  mikrosekund mms = 12345; 
	  milisekund ms = 123; 
	  sekund s = 10; 
	  minut m = 30; 
	  Zrušení h = 34; 

	  auto x = std :: chrono :: hodin (3); // je výslovně o jmenných 
	  auto x = hodiny (2) + minut (35) + sekund (9); // za předpokladu, že vhodné "pomocí" 

Nemůžete inicializovat trvání na zlomek. Například, nesnažte 2,5 sekundy, místo použití 2500 milisekund. To je proto, že, protože doba je interpretován jako počet `` klíšťat.'' Každý tick představují v jednotce doba je `` období,'' jako milli a Kilo, jak jsou definovány výše. Výchozí jednotkou sekund, to znamená, že pro dobu s dobou 1 klíště je interpretován jako druhý. Můžeme být explicitní o zastoupení v délce:

 
	  Doba trvání <long> d0 = 5; // sekund (standardně) 
	  Doba trvání <long,kilo> d1 = 99; // kiloseconds! 
	  délka <dlouhá, poměr <1000,1 >> d2 = 100; // d1 a d2 mít stejný typ ("kilo" znamená "* 1000") 

Pokud chceme skutečně udělat něco s dobou trvání, jako je psaní na to, musíme se vzdát jednotku, například minut nebo mikrosekundách Například.:

 
	  auto t = monotonic_clock :: now (); 
	  // Něco 
	  nanosekund d = monotonic_clock :: now () - t; // chceme výsledek v nanosekund 
	  cout << "něco vzal" << d << "nanosekund \\\\n"; 
 

Případně můžeme převést trvání na desetinné číslo (dostat zaokrouhlení):

 
	  auto t = monotonic_clock :: now (); 
	  // Něco 
	  auto d = monotonic_clock :: now () - t; 
	  cout << "něco vzal" << trvání <double> (d) počítat () << "sekund \\\\n".; 
 

Count () je počet `` klíšťat.''

Viz vzít


Atomové

Je nám líto, neměl jsem čas psát tuto položku. Prosim, vraťte sobě později.

Viz vzít

  • Standardní: 29 Atomic operace knihovna [atomové]
  • ?

std :: budoucnost a std :: slib

Souběžné programování může být těžké, zvláště pokud se budete snažit být chytrý s nití a zámky . Je to ještě těžší, pokud je nutné použít kondiční proměnné nebo použít std-atomové (pro zámek bez programování). C + 11 nabízí budoucnost a slib pro vrácení hodnoty z úkolu plodil na samostatné vlákno a packaged_task pomoci zahájit úkoly. Důležitým bodem o budoucnosti a příslib je, že umožňují převod hodnoty mezi dvěma úkoly bez výslovného použití zámku, `` systém'' implementuje převod efektivně. Základní myšlenka je jednoduchá: Pokud úkol chce vrátit hodnotu na závit, který zahájil, klade hodnotu do slibu. Nějak, implementace je, že hodnota se zobrazí v budoucnu připojené k slibu. Volající (obvykle odpalovací úkolu) pak může přečíst hodnotu. Pro zvýšení jednoduchosti, viz async () .

Norma uvádí tři druhů futures, budoucí pro většinu jednoduchých použití, a shared_future a atomic_future pro některé složitější případy. Tady, budu jen představit budoucnost, protože je to nejjednodušší a to všechno, co potřebuju udělat. Pokud máme v budoucnu <X> názvem f, dostaneme () hodnotu typu X z toho:

 
	  X v = f.get (); // pokud je to nutné čekat na hodnotu dostat počítá 

Pokud hodnota není tam ještě, je naše vlákno zablokováno, dokud to přijde. Pokud hodnota nebylo možné vypočítat, může výsledek get () je k vyvolání výjimky (ze systému nebo přenášena z úkolu, ze které jsme se snažili dostat () hodnotu.

Mohli bychom nechtěli čekat na výsledek, takže se můžeme ptát na budoucnost, když výsledek je tady:

 
	  if (f.wait_for (0)) {// je hodnota dostat () 
		  // Něco 
	  } 
	  else { 
		  // Něco jiného 
	  } 

Nicméně, hlavním účelem budoucnosti je poskytnout tak jednoduché dostat ().

Hlavním účelem slibu je poskytnout jednoduchý `` dát'' (kupodivu, tzv. `` set''), aby odpovídaly budoucnost je get (). Jména `` budoucí'' a `` slib'' jsou historické, prosím neobviňujte mě. Jsou také úrodný zdroj hříček.

Pokud máte slib a je třeba poslat výsledek typu X (zpět) do budoucnosti, existují v zásadě dvě věci, které můžete udělat: předat hodnotu a předat výjimku:

 
	  try { 
		  X res; 
		  // Výpočet hodnoty pro OZE 
		  p.set_value (res); 
	  } 
	  catch (...) {// oops: nelze spočítat res 
		  p.set_exception (std :: current_exception ()); 
	  } 

Zatím je to dobré, ale jak se můžu dostat odpovídající budoucí / slib pár, jeden v mé vlákno a jeden v nějakém jiném vlákně? No, protože budoucnost je a slib s může pohybovat (ne kopírovat) kolem je široká škála možností. Nejvíce zřejmá myšlenka je pro každého, kdo chce udělat úkol vytvořit vlákno a dát slib, aby to při zachování odpovídající budoucnost jako místo pro výsledek. Použití asynchronní () je nejkrajnější / elegantní varianta druhé techniky.

Typ packaged_task je poskytována zjednodušení zahájení vlákno vykonat úkol. Zejména se stará o zřízení budoucnost připojený na slib a poskytuje souhrnný kód dát návratovou hodnotu nebo výjimku z úkolu do slibu. Například:

 
	  double vzorek (vector <double> & v) 
	  { 
		  // Zabalit úkoly: 
		  // (Úkolem zde je standardní akumulovat () pro pole se zdvojnásobí): 
		  packaged_task <double(double*,double*,double)> PT0 {std :: hromadí <double*,double*,double>}; 
		  packaged_task <double(double*,double*,double)> pt1 {std :: hromadí <double*,double*,double>}; 

		  auto f0 = pt0.get_future (); // sehnat futures 
		  auto f1 = pt1.get_future (); 

		  PT0 (& v [0], a v [v.size () / 2], 0); // spuštění vlákna 
		  pt1 (& v [v.size () / 2], a v [size ()], 0); 
	
		  návrat f0.get () + f1.get (); // získat výsledky 
	  } 

Viz vzít


std :: async ()

Async () jednoduchý úkol launcher funkce je jediným pracovištěm v tomto FAQ, který nebyl ještě schválen do návrhu normy. Očekávám, že se bude hlasovat v v říjnu po vyvážení dvou Slighty různé návrhy (klidně říct na místní člen výboru, aby se ujistěte se, že pro něj hlasovat).

Zde je příklad jak pro programátor povznést se nad chaotický závity-plus-lock úrovni souběžné programování:

 
	  template <class T, třída V> struct Accum {// jednoduchý akumulátor objekt funkce 
		  T * b; 
		  T * e; 
		  V val; 
		  Accum (T * bb, T * ee, const V & V): b {bb}, {e ee}, {val vv} {} 
		  V operátor () () {return std :: hromadit (b, e, val);} 
	  }; 

	  double vzorek (vector <double> & v) 
		  // Spawn mnoho úkolů, pokud v je dostatečně velký 
	  { 
		  if (v.size () <10000) return std :: hromadit (v.begin (), v.end (), 0,0); 

		  auto f0 {async (Accum {& v [0], a v [v.size () / 4], 0,0})}; 
		  auto f1 {async (Accum {& v [v.size () / 4], a v [v.size () / 2], 0,0})}; 
		  auto f2 {async (Accum {& v [v.size () / 2], a v [v.size () * 3/4], 0,0})}; 
		  auto f3 {async (Accum {& v [v.size () * 3/4], a v [v.size ()], 0,0})}; 

		  návrat f0.get () + f1.get () + f2.get () + f3.get (); 
	  } 

To je velmi prostoduchý použití souběžnosti (všimněte si `` magické číslo''), ale upozornit na absenci výslovné závit s, zámek s, nárazníky, atd. typu F-proměnné jsou určeny návratový typ ze standardní knihovny funkcí async (), což je budoucnost . Pokud je to nutné, get () na budoucnosti čeká na vlákno až do konce. Zde je async () 's úkolem, aby se třeli vlákno s podle potřeby a budoucí "s práci join () závitu s odpovídajícím. `` Simple'' je nejdůležitějším aspektem asynchronov () / budoucí designu, futures může být také použit s vlákny obecně, ale ani že použití asynchronní () zahájit úkoly, které dělat I / O, manipuluje mutexy, nebo jinými způsoby interakce s jinými úkoly. Myšlenka asynchronov () je stejný jako myšlenkou rozsah-pro výraz: Poskytnout jednoduchý způsob, jak zvládnout nejjednodušší, ale společné, věci a opustit složitější příklady plně obecný mechanismus.

Async () může být požadováno, aby v roce nové vlákno, v každém závitu, ale volajícího, nebo spustit v jiném podprocesu, pouze pokud async () `` myslí'', že je to dobrý nápad. Ta je nejjednodušší z pohledu uživatele a potenciálně nejúčinnější (pro jednoduché úkoly (pouze)).

Viz vzít


opuštění procesu

Je nám líto, neměl jsem čas psát tuto položku. Prosim, vraťte sobě později.


Generování náhodných čísel

Náhodná čísla jsou užitečné v mnoha kontextech, jako je testování, hry, simulace, a bezpečnost. Rozmanitost aplikačních oblastí se odráží v široké nabídce generátorů náhodných čísel poskytnutých standardní knihovny. Generátor náhodných čísel se skládá ze dvou částí motoru, který produkuje sekvence náhodných nebo pseudonáhodných hodnot a distribuce, který mapuje tyto hodnoty na základě matematického distribuci v rozsahu. Příklady distribucí jsou uniform_int_distribution (kde jsou všechny vyrobené čísla jsou stejně pravděpodobné) a normal_distribution (`` křivka zvonku''), každá z nějakého určeném rozsahu Například:.

 
	  uniform_int_distribution <int> one_to_six {1,6}; // distribuce, která mapuje na ints 1 .. 6 
	  default_random_engine re {} // implicitní stroj 

Chcete-li získat náhodné číslo, zavoláte distribuci s motorem:

 
	  int x = one_to_six (re); // x se hodnota v [01:06] 

Předávání motor v každém hovoru lze považovat za nudné, a tak jsme mohli svázat tento argument získat objekt funkce, které můžeme nazvat bez argumentů:

 
	  auto kostky {bind (one_to_six, re)}; // vytvořit generátor 

	  int x = kostky (); // kostky: x se stává hodnota v [01:06] 

Díky své nekompromisním důrazem na obecnosti a výkon jednoho odborníka považuje za standardní knihovny náhodné číslo komponentu ``, co každý náhodné číslo knihovna Co se kdy vyroste.'' Však lze jen těžko považovat za `` nováček přátelský.'' Nikdy jsem našel náhodné číslo rozhraní bude představení láhev krk, ale já jsem nikdy učil nováčky (jakékoliv pozadí), aniž by potřebovali velmi jednoduchý generátor náhodných čísel. To by mělo stačit

 
	  int rand_int (int low, high); // generovat náhodné číslo z rovnoměrného rozdělení na [low: vysoká] 

Tak, jak by jsme se, že? Musíme se dostat něco jako kostky () uvnitř rand_int ():

 
	  int rand_int (int low, high int) 
	  { 
		  static default_random_engine re {}; 
		  pomocí Dist = uniform_int_distribution <int>; 
  		  static Dist uid {}; 
  		  návrat uid (re, Dist :: param_type {low, high}); 
	  } 

Tato definice je stále `` úrovni odborníků'', ale použití rand_int () zvládnout v průběhu prvního týdne C + + samozřejmě.

Stačí ukázat non-triviální příklad, tady je program, který generuje a tiskne normální rozdělení:

 
	  default_random_engine re, // implicitní stroj 
	  normal_distribution <int> nd (31 / * znamená * /, 8 / * sigma * /); 

	  auto norm = std :: bind (nd, re); 

	  vektor <int> mn (64); 

	  int main () 
	  { 
		  for (int i = 0; i <1200; + + i) + + mn [kolo (norm ())]; // generovat 
	
		  for (int i = 0; i <mn.size (); + + i) { 
			  cout << i << "\\\\t '; 
			  for (int j = 0; j <mn [i]; + + j) cout << "*"; 
			  cout << "\\\\n '; 
		  } 
	  } 

Výsledek byl:


0	
1	
2	
3	
4	*
5	
6	
7	
8	
9	*
10	***
11	***
12	***
13	*****
14	*******
15	****
16	**********
17	***********
18	****************
19	*******************
20	*******************
21	**************************
22	**********************************
23	**********************************************
24	********************************************
25	*****************************************
26	*********************************************
27	*********************************************************
28	***************************************************
29	******************************************************************
30	**********************************************
31	*********************************************************************
32	**********************************************
33	*************************************************************
34	**************************************************************
35	***************************************
36	***********************************************
37	**********************************************
38	*********************************************
39	********************************
40	********************************************
41	***********************
42	**************************
43	******************************
44	*****************
45	*************
46	*********
47	********
48	*****
49	*****
50	****
51	***
52	***
53	**
54	*
55	*
56	
57	*
58	
59	
60	
61
62
63

Viz vzít

  • Standardní 26.5: generování náhodných čísel

Regulární výrazy

  • 28 Regular výrazy knihovna

Je nám líto, neměl jsem čas psát tuto položku. Prosim, vraťte sobě později.

Viz vzít

  • Standardní:??
  • ?

Pojmy

Upozornění: "koncepty" nedělal to do C + 11 a radikální redesign probíhá

"Pojmy" je mechanismus pro popis požadavků na typy, kombinace typů, a kombinací typů a celá čísla. To je užitečné zejména pro získání včasného kontrolu použití šablon. Naopak, to také pomáhá včasné odhalení chyb v šabloně těle. Zvažte standardní knihovna algoritmus vyplnit:

	 šablona <ForwardIterator Iter, třída V> // druhy typů
		 vyžaduje Přiřaditelné <Iter::value_type,V> // vztahy mezi argumentů typů
	 void fill (Iter první, Iter poslední, const V & V); // jen prohlášení, ne definice


	 vyplnit (0, 9, 9.9); // Iter je int, chyba: int není ForwardIterator
					 // Int nemá předponu *
	 vyplňte (a v [0], a v [9], 9,9); // Iter je int, ok: int * je ForwardIterator

Všimněte si, že jsme jen prohlásil fill (); jsme neměli definovat (poskytne jeho provádění). Na druhé straně, jsme výslovně uvedeno, jaké vyplnit () vyžaduje od svého tvrzení:

  • Argumenty První a poslední musí být typu, který je ForwardIterator (a musí být stejného typu).
  • Třetí argument proti musí být typu, který může být přiřazen k ForwardIterator s VALUE_TYPE.

Věděli jsme, že, samozřejmě, že si standard. Nicméně, překladače nečtou požadavek dokumenty, takže jsme museli říct, že v kódu pomocí koncepty ForwardIterator a přiřazení. Výsledkem je, že chyby v použití vyplnit () jsou zachyceny ihned v místě použití, a že chybová hlášení jsou výrazně lepší. Kompilátor má nyní informace o záměrech programátoři ", který umožňuje dobrou kontrolu a dobré diagnostiku.

Koncepty také pomoci šablony pro nasazování. Zvažte:

	 šablona <ForwardIterator Iter, třída V>
		 vyžaduje Přiřaditelné <Iter::value_type,V>
	 void fill (Iter první, Iter poslední, const V & V)
	  {
		 while (first! = last) {
			 * První = v;
			 první = první 1, // ​​error: + není definován pro Forward_iterator
					 // (Použijte + + první)
		  }
	  }

Tato chyba je chycen okamžitě, což eliminuje potřebu hodně nudné testování (i když samozřejmě ne všechny zkoušky).

Schopnost klasifikovat a rozlišení různých typů typů můžeme přetížení v závislosti na druhu předaných typů. Například

	 // Iterátor na bázi standardu sort (s pojmy):
	 šablona <Random_access_iterator Iter>
		 vyžaduje, aby se srovnatelnými <Iter::value_type>
	 void sort (Iter první, Iter poslední); // používat obvyklé provádění

	 // Kontejner-based sort:
	 šablona <Container Cont>
		 vyžaduje, aby se srovnatelnými <Cont::value_type>
	 void sort (Cont & c)
	  {
		 sort (c.begin (), c.end ()); // jednoduše zavolejte iterátor verzi
	  }

	 void f (vector <int> & v)
	  {
		 sort (v.begin (), v.end ()); // jedna cesta
		 sort (v); // jiný způsob
		  // ...
	  }

Můžete definovat své vlastní představy, ale pro začátek standardní knihovna poskytuje celou řadu užitečných pojmů, jako je ForwardIterator, Disponibilní, LessThanComparable, a Regular.

Poznámka: C + +0 x standardní knihovny byly specifikovány pomocí pojmů.

Viz vzít


Koncepce mapy

Int * je ForwardIterator, my jsme řekli při prezentaci koncepce , standardní vždy říkal, a dokonce i první verze STL používat ukazatele jako iterátory. Nicméně, jsme také mluvili o ForwardIterator 's VALUE_TYPE. Ale int * nemá člen s názvem value_type, ve skutečnosti, že nemá žádné členy. Jak tedy může int * být ForwardIterator? Je to proto, že říkáme, že je. Pomocí concept_map, můžeme říci, že pokud je * T používají tam, kde je vyžadována ForwardIterator, považujeme T jeho value_type:

	 šablona <Value_type T> 
	 concept_map ForwardIterator <T*> {// T * 's value_type je T
		 typedef T value_type;
	  };

Concept_map nám umožňuje říci, jak chceme, aby typ, šetří nám z nutnosti upravit nebo ji zabalte do nového typu. "Koncept mapy" je velmi flexibilní a obecný mechanismus pro úpravu nezávisle vyvinutý software pro běžné použití.


Axiomy

Axiom je soubor predikátů specifikující sémantiku pojmu. Primární použití pouzdra pro axiomy jsou externí nástroje (např. není obyčejný překladač akce), jako nástroje pro domény specifické optimalizace (jazyky pro určení programu transformace byly významnou součástí motivace pro axiomů). Druhotné využití je prostě přesná specifikace sémantiky v normě (jak je používán v mnoha částech standardní knihovny specifikace). Axiomy mohou být také užitečné pro některé optimalizace (provádí kompilátory a tradiční optimalizující), ale překladače nemusí všímat uživatelem dodaných axiomů, které pracují na základě sémantiky definovaných v normě.

Axiom uvádí dvojice výpočtů, které mohou být považovány za rovnocenné. Zvažte:

	 Koncepce pologrupa <typename Op, typename T>: CopyConstructible <T> {
		 T operator () (Op, T, T);
		 axiom asociativita (OP OP, T x, T y, T z) {
			 op (x, op (y, z)) <=> op (op (x, y), z); // t operátor může být považována za asociativní
		  }
	  }

	 Koncepce monoid <typename Op, typename T>: pologrupa <Op, T> {// monoid je pologrupa s elementem identity
		 T identity_element (Op);
		 axiom Identity (OP OP, T x) {
			 op (x, identity_element (op)) <=> x;
			 op (identity_element (op), x) <=> x;
		  }
	  }

<=> Je ekvivalence operátor, který se používá pouze v axiomů. Všimněte si, že nemůžete (obecně) se ukázala jako axiom, používáme axiomy uvést to, co nemůžeme dokázat, ale to, co programátor lze konstatovat, že je přijatelné předpoklad. Všimněte si, že obě strany o rovnocennosti prohlášení může být nelegální pro některé hodnoty, např. použití NaN (není číslo) pro plovoucí desetinnou čárkou typu: pokud obě strany na rovnocennosti používá NaN oba jsou (samozřejmě) za neplatné, a ekvivalentní ( nezávisle, co axiom říká), ale pokud jen jedna strana používá NaN může být příležitostí pro využití axiomu.

Axiom je posloupnost rovnocennosti závěrky (pomocí <=>) a podmíněné příkazy (ve tvaru "if (něco), pak můžeme předpokládat, následující ekvivalence"):

	 // V konceptu TotalOrder:
	 axiom tranzitivity (OP OP, T x, T y, T z)
	  {
		 if (op (x, y) && op (y, z)) op (x, z) <=> true; // podmíněná rovnocennosti 
	  }

Viz vzít

  • C + + návrh 14.9.1.4 Axiomy
  • ?? .

Original version: http://www.stroustrup.com/C++11FAQ.html