C ++ 11 - C++11

C ++ 11 je verzí normy ISO / IEC 14882 pro programovací jazyk C ++ . C ++ 11 nahradil předchozí verzi standardu C ++ s názvem C ++ 03 a později byl nahrazen C ++ 14 . Název navazuje na tradici pojmenovávání jazykových verzí do roku vydání specifikace, i když dříve dostal název C ++ 0x, protože se očekávalo, že bude vydán před rokem 2010.

Ačkoli jedním z cílů návrhu bylo upřednostnit změny knihoven před změnami v základním jazyce , C ++ 11 provede několik dodatků k základnímu jazyku. Mezi oblasti, které byly podstatně vylepšeny, patří podpora více vláken, podpora generického programování , jednotná inicializace a výkon. Významné změny byly provedeny také ve standardní knihovně C ++ , zahrnující většinu knihoven C ++ Technical Report 1 (TR1) , kromě knihovny matematických speciálních funkcí.

C ++ 11 byl publikován jako ISO/IEC 14882: 2011 v září 2011 a je k dispozici za poplatek. Pracovní koncept nejpodobnější publikované normě C ++ 11 je N3337 ze dne 16. ledna 2012; má pouze redakční opravy ze standardu C ++ 11.

Konstrukční cíle

Návrhová komise se pokusila dodržet řadu cílů při navrhování C ++ 11:

  • Udržujte stabilitu a kompatibilitu s C ++ 98 a případně s C
  • Upřednostňujte zavádění nových funkcí prostřednictvím standardní knihovny před rozšířením základního jazyka
  • Preferujte změny, které mohou vyvinout techniku ​​programování
  • Vylepšujte C ++, abyste usnadnili návrh systémů a knihoven, než zavádějte nové funkce užitečné pouze pro konkrétní aplikace
  • Zvyšte bezpečnost typů poskytováním bezpečnějších alternativ k dřívějším nebezpečným technikám
  • Zvyšte výkon a schopnost pracovat přímo s hardwarem
  • Poskytněte správná řešení problémů v reálném světě
  • Implementujte princip nulové režie (další podpora vyžadovaná některými nástroji musí být použita pouze v případě, že je nástroj používán)
  • Usnadněte výuku a učení se C ++, aniž byste museli odstraňovat nástroje, které potřebují zkušení programátoři

Pozornost pro začátečníky je považována za důležitou, protože většina počítačových programátorů bude vždy taková, a protože mnoho začátečníků nikdy nerozšíří své znalosti, omezí se na práci v aspektech jazyka, na který se specializují.

Rozšíření na základní jazyk C ++

Jednou z funkcí výboru C ++ je vývoj jazykového jádra. Mezi oblasti, které byly podstatně vylepšeny, patří podpora více vláken, podpora generického programování , jednotná inicializace a výkon.

Vylepšení výkonu jádra za běhu

Tyto jazykové funkce primárně existují, aby poskytovaly určitý druh výkonu, ať už paměti nebo výpočetní rychlosti.

Rvalue reference and move constructors

V C ++ 03 (a dříve) byly dočasné (nazývané „ rvalues “, protože často leží na pravé straně úkolu) zamýšleny tak, aby nikdy nebyly modifikovatelné - stejně jako v C - a byly považovány za nerozeznatelné od const T&typů; nicméně v některých případech bylo možné dočasně upravit, což bylo chování, které bylo dokonce považováno za užitečnou mezeru. C ++ 11 přidává nový nekonstantní referenční typ s názvem anrvalue reference , identifikovánT&&. To se týká dočasných povolených úprav po jejich inicializaci za účelem povolení „pohybové sémantiky“.

Chronickým problémem výkonu v C ++ 03 jsou nákladné a nepotřebné hluboké kopie, ke kterým může dojít implicitně, když jsou objekty předávány podle hodnoty. Pro ilustraci problému zvažte, že std::vector<T>je interně obálkou kolem pole ve stylu C s definovanou velikostí. Pokud je std::vector<T>dočasný vytvořen nebo vrácen z funkce, lze jej uložit pouze vytvořením nového std::vector<T>a zkopírováním všech dat rvalue do něj. Poté je dočasná a veškerá její paměť zničena. (Pro jednoduchost tato diskuse opomíjí optimalizaci návratové hodnoty .)

V C ++ 11, a move constructor ofstd::vector<T>that takes an rvalue reference to anstd::vector<T>can copy the pointer to the internal C-style array out of the rvalue into the newstd::vector<T>, then set the pointer inside the rvalue to null. Vzhledem k tomu, že dočasné již nebude nikdy použito, žádný kód se nepokusí získat přístup k nulovému ukazateli a protože ukazatel je nulový, jeho paměť není odstraněna, když se dostane mimo rozsah. Operace tedy nejenže zříká nákladů na hlubokou kopii, ale je bezpečná a neviditelná.

Reference Rvalue mohou poskytovat výhody pro stávající kód, aniž by bylo nutné provádět jakékoli změny mimo standardní knihovnu. Typ vrácené hodnoty funkce, která vrací std::vector<T>dočasnou, není nutné explicitně měnit tak, std::vector<T> &&aby vyvolával konstruktor přesunu, protože dočasné hodnoty jsou automaticky považovány za hodnoty rvalues. (Pokud však std::vector<T>jde o verzi C ++ 03 bez konstruktoru přesunu, bude konstruktor kopie vyvolán pomocí, což způsobí const std::vector<T>&významné přidělení paměti.)

Z bezpečnostních důvodů jsou uložena určitá omezení. Pojmenovaná proměnná nebude nikdy považována za hodnotu rvalue, i když je jako taková deklarována. Chcete -li získat hodnotu rvalue, std::move()měla by být použita šablona funkce . Odkazy Rvalue lze také upravovat pouze za určitých okolností, přičemž jsou určeny k použití primárně s konstruktory tahů.

Vzhledem k povaze znění odkazů rvalue a některé úpravě znění pro odkazy lvalue (pravidelné odkazy) umožňují odkazy rvalue vývojářům poskytovat dokonalé předávání funkcí. V kombinaci s variadickými šablonami tato schopnost umožňuje šablony funkcí, které dokážou perfektně předat argumenty jiné funkci, která tyto konkrétní argumenty přebírá. To je nejužitečnější pro předávání parametrů konstruktoru, k vytváření továrních funkcí, které automaticky zavolají správný konstruktor pro tyto konkrétní argumenty. To je vidět v sadě emplace_back standardních metod knihovny C ++.

constexpr - generalizované konstantní výrazy

C ++ měl vždy koncept konstantních výrazů. Toto jsou výrazy, jako 3+4že vždy poskytnou stejné výsledky, v době kompilace a za běhu. Konstantní výrazy představují pro kompilátory příležitost k optimalizaci a kompilátory je často provádějí v době kompilace a výsledky v programu pevně zakódují. Specifikace C ++ také na několika místech vyžaduje použití konstantních výrazů. Definování pole vyžaduje konstantní výraz a hodnoty enumerátoru musí být konstantní výrazy.

Konstantní výraz však nikdy nesměl obsahovat volání funkce nebo konstruktor objektu. Takže tak jednoduchý kód, jako je tento, je neplatný:

int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++

Toto nebylo platné v C ++ 03, protože get_five() + 7to není konstantní výraz. Kompilátor C ++ 03 nemá způsob, jak zjistit, zda get_five()je ve skutečnosti za běhu konstantní. Tato funkce by teoreticky mohla ovlivnit globální proměnnou, zavolat další funkce konstant bez běhu atd.

C ++ 11 zavedlo klíčové slovo constexpr, které umožňuje uživateli zaručit, že konstruktor funkce nebo objektu je časová konstanta kompilace. Výše uvedený příklad lze přepsat následujícím způsobem:

constexpr int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Valid C++11

To umožňuje kompilátoru pochopit a ověřit, že get_five()je to časová konstanta kompilace.

Použití constexprfunkce ukládá určitá omezení, co tato funkce dokáže. Za prvé, funkce musí mít nevratný návratový typ. Za druhé, tělo funkce nemůže deklarovat proměnné ani definovat nové typy. Za třetí, tělo může obsahovat pouze deklarace, nulové příkazy a jediné prohlášení o návratu. Musí existovat takové hodnoty argumentů, aby po nahrazení argumentu výraz v příkazu return vytvořil konstantní výraz.

Před C ++ 11 mohly být hodnoty proměnných použity v konstantních výrazech pouze v případě, že jsou proměnné deklarovány jako const, mají inicializátor, což je konstantní výraz, a jsou integrálního nebo výčtového typu. C ++ 11 odstraní omezení, že proměnné musí být integrálního typu nebo typu výčtu, pokud jsou definovány constexprklíčovým slovem:

constexpr double earth_gravitational_acceleration = 9.8;
constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0;

Takové datové proměnné jsou implicitně konstantní a musí mít inicializátor, který musí být konstantní výraz.

Aby bylo možné konstruovat hodnoty dat konstantního výrazu z typů definovaných uživatelem, mohou být konstruktory také deklarovány pomocí constexpr. A constexprkonstruktérů tělo funkce může obsahovat pouze prohlášení a prohlášení o nulové, a nemůže deklarovat proměnné nebo definovat typy, jako u constexprfunkce. Musí existovat takové hodnoty argumentů, aby po nahrazení argumentů inicializovalo členy třídy konstantními výrazy. Destruktory pro takové typy musí být triviální.

Kopírovací konstruktor pro typ s libovolnými constexprkonstruktory by měl být obvykle také definován jako constexprkonstruktor, aby bylo možné objekty typu vrátit hodnotou z funkce constexpr. Libovolnou členskou funkci třídy, jako jsou konstruktory kopií, přetížení operátorů atd., Lze deklarovat jako constexpr, pokud splňují požadavky na funkce constexpr. To umožňuje kompilátoru kopírovat objekty v době kompilace, provádět s nimi operace atd.

Pokud je funkce nebo konstruktor constexpr volána s argumenty, které nejsou konstantními výrazy, volání se chová, jako by funkce nebyla constexpr, a výsledná hodnota není konstantní výraz. Podobně, pokud výraz v příkazu return funkce constexpr nevyhodnocuje konstantní výraz pro dané vyvolání, není výsledek konstantní výraz.

constexprse liší od consteval, zavedeného v C ++ 20 , v tom, že druhý musí vždy vytvářet časovou konstantu kompilace, přičemž constexprtoto omezení nemá.

Úprava definice obyčejných starých dat

V jazyce C ++ 03 musí třída nebo struktura dodržovat řadu pravidel, aby mohla být považována za typ obyčejných starých dat (POD). Typy, které odpovídají této definici, vytvářejí rozložení objektů, která jsou kompatibilní s C, a lze je také inicializovat staticky. Standard C ++ 03 má omezení na to, jaké typy jsou kompatibilní s C nebo je lze staticky inicializovat, přestože kompilátor nemohl přijmout program; pokud by někdo vytvořil typ POD C ++ 03 a přidal nevirtuální členskou funkci, tento typ by již nebyl typ POD, nemohl by být staticky inicializován a byl by nekompatibilní s C i přes žádnou změnu rozložení paměti .

C ++ 11 uvolnil několik pravidel POD tím, že koncept POD rozdělil na dva samostatné koncepty: triviální a standardní .

Triviální typ lze staticky inicializovat. To také znamená, že je platné kopírovat data kolem prostřednictvím memcpy, spíše než používat kopírovací konstruktor. Životnost triviálního typu začíná okamžikem, kdy je definováno jeho úložiště, nikoli dokončením konstruktoru.

Triviální třída nebo struktura je definována jako ta, která:

  1. Má triviální výchozí konstruktor. To může použít výchozí syntaxi konstruktoru ( SomeConstructor() = default;).
  2. Má triviální kopírování a přesouvání konstruktorů, které mohou používat výchozí syntaxi.
  3. Má triviální operátory přiřazení kopírování a přesouvání, které mohou používat výchozí syntaxi.
  4. Má triviální destruktor, který nesmí být virtuální.

Konstruktory jsou triviální pouze v případě, že neexistují žádné virtuální členské funkce třídy a žádné virtuální základní třídy. Operace kopírování/přesouvání také vyžadují, aby všechny nestatické datové členy byly triviální.

Typ, který má standardní rozložení, znamená, že objednává a balí své členy způsobem, který je kompatibilní s C. Třída nebo struktura je standardní rozložení podle definice za předpokladu:

  1. Nemá žádné virtuální funkce
  2. Nemá žádné virtuální základní třídy
  3. Všechny jeho nestatické datové členy mají stejné řízení přístupu (veřejné, soukromé, chráněné)
  4. Všechny jeho nestatické datové členy, včetně všech v základních třídách, jsou v hierarchii ve stejné třídě
  5. Výše uvedená pravidla platí také pro všechny základní třídy a pro všechny nestatické datové členy v hierarchii tříd
  6. Nemá žádné základní třídy stejného typu jako první definovaný nestatický datový člen

Třída/struktura/unie je považována za POD, pokud je triviální, má standardní rozložení a všechny její nestatické datové členy a základní třídy jsou POD.

Oddělením těchto konceptů je možné jednoho se vzdát, aniž byste ztratili druhý. Třída s komplexními konstruktory přesunu a kopírování nemusí být triviální, ale může být standardní rozložení, a tedy spolupracovat s C. Podobně třída s veřejnými a soukromými nestatickými datovými členy by nebyla standardní rozložení, ale mohla by být triviální a tím memcpypádem použitelné.

Vylepšení výkonu při vytváření základního jazyka

Externí šablona

V jazyce C ++ 03 musí kompilátor vytvořit instanci šablony pokaždé, když se v překladové jednotce objeví plně zadaná šablona. Pokud je šablona vytvořena u stejných typů v mnoha překladových jednotkách, může to dramaticky prodloužit dobu kompilace. V C ++ 03 tomu nelze zabránit, takže C ++ 11 zavedlo deklarace externích šablon, analogické deklaracím externích dat.

C ++ 03 má tuto syntaxi, která kompilátoru ukládá instanci šablony:

template class std::vector<MyClass>;

C ++ 11 nyní poskytuje tuto syntaxi:

extern template class std::vector<MyClass>;

který říká kompilátoru , aby v této překladové jednotce neinicioval šablonu.

Vylepšení základní použitelnosti jazyka

Tyto funkce existují především za účelem snazšího používání jazyka. Ty mohou zlepšit bezpečnost typu, minimalizovat opakování kódu, snížit pravděpodobnost chybného kódu atd.

Seznamy inicializátorů

C ++ 03 zdědil funkci seznamu inicializátorů od C. Struktuře nebo poli je uveden seznam argumentů v závorkách v pořadí definic členů ve struktuře. Tyto seznamy inicializátorů jsou rekurzivní, takže je může použít řada struktur nebo struktur obsahujících jiné struktury.

struct Object
{
    float first;
    int second;
};

Object scalar = {0.43f, 10}; //One Object, with first=0.43f and second=10
Object anArray[] = {{13.4f, 3}, {43.28f, 29}, {5.934f, 17}}; //An array of three Objects

To je velmi užitečné pro statické seznamy nebo inicializaci struktury na nějakou hodnotu. C ++ také poskytuje konstruktory k inicializaci objektu, ale často nejsou tak pohodlné jako seznam inicializátorů. C ++ 03 však umožňuje seznamy inicializátorů pouze u struktur a tříd, které odpovídají definici POD (Plain Old Data); C ++ 11 rozšiřuje seznamy inicializátorů, takže je lze použít pro všechny třídy včetně standardních kontejnerů jako std::vector.

C ++ 11 váže koncept na šablonu s názvem std::initializer_list. To umožňuje konstruktorům a dalším funkcím převzít seznamy inicializátorů jako parametry. Například:

class SequenceClass
{
public:
    SequenceClass(std::initializer_list<int> list);
};

To umožňuje SequenceClasssestavit ze sekvence celých čísel, jako například:

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

Tento konstruktor je speciální druh konstruktoru, který se nazývá inicializátor-seznam-konstruktor. Třídy s takovým konstruktorem jsou zpracovávány speciálně během jednotné inicializace (viz níže )

Třída šablony std::initializer_list<>je prvotřídní standardní typ knihovny C ++ 11. Mohou být vytvořeny staticky kompilátorem C ++ 11 pomocí {}syntaxe bez názvu typu v kontextech, kde se taková závorka odvozuje od an std::initializer_list, nebo explicitním zadáním typu jako std::initializer_list<SomeType>{args}(a tak dále pro jiné varianty stavební syntaxe).

Seznam lze zkopírovat po sestavení, což je levné a bude fungovat jako kopírování podle odkazu (třída je obvykle implementována jako dvojice ukazatelů začátku/konce). An std::initializer_listje konstantní: jeho členy nelze po vytvoření změnit ani data v těchto členech (což vylučuje přesun z nich, vyžadování kopií do členů třídy atd.).

Ačkoli je jeho konstrukce speciálně zpracována kompilátorem, an std::initializer_listje skutečný typ, a proto ho lze použít i na jiných místech kromě konstruktorů tříd. Běžné funkce mohou považovat zadané std::initializer_lists za argumenty. Například:

void function_name(std::initializer_list<float> list); // Copying is cheap; see above

function_name({1.0f, -3.45f, -0.4f});

Mezi příklady ve standardní knihovně patří šablony std::min()a std::max()šablony std::initializer_lists číslicového typu.

Standardní kontejnery lze také inicializovat těmito způsoby:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v({ "xyzzy", "plugh", "abracadabra" });
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; // see "Uniform initialization" below

Jednotná inicializace

C ++ 03 má řadu problémů s inicializací typů. Existuje několik způsobů, jak toho dosáhnout, a některé při výměně přinášejí různé výsledky. Tradiční syntaxe konstruktoru může například vypadat jako deklarace funkce a je třeba učinit kroky k zajištění toho, aby si to nejpřísnější pravidlo kompilátoru kompilátoru za takové nezaměnilo . Pomocí agregačních inicializátorů (pomocí SomeType var = {/*stuff*/};) lze inicializovat pouze agregáty a typy POD .

C ++ 11 poskytuje syntaxi, která umožňuje plně jednotnou inicializaci typu, která funguje na libovolném objektu. Rozbalí se na syntaxi seznamu inicializátorů:

struct BasicStruct
{
    int x;
    double y;
};

struct AltStruct
{
    AltStruct(int x, double y)
        : x_{x}
        , y_{y}
    {}

private:
    int x_;
    double y_;
};

BasicStruct var1{5, 3.2};
AltStruct var2{2, 4.3};

Inicializace se var1chová přesně tak, jako by šlo o agregační inicializaci. To znamená, že každý datový člen objektu bude následně inicializován kopírováním s odpovídající hodnotou ze seznamu inicializátorů. V případě potřeby se použije implicitní převod typů. Pokud žádná konverze neexistuje, nebo existuje pouze zužující se konverze, je program špatně vytvořen. Inicializace var2invokuje konstruktor.

Lze také provést toto:

struct IdString
{
    std::string name;
    int identifier;
};

IdString get_string()
{
    return {"foo", 42}; //Note the lack of explicit type.
}

Jednotná inicializace nenahrazuje syntaxi konstruktoru, která je občas stále potřebná. Pokud má třída konstruktor seznamu inicializátorů ( TypeName(initializer_list<SomeType>);), pak má přednost před jinými formami konstrukce za předpokladu, že seznam inicializátorů odpovídá typu konstruktoru sekvence. Verze C ++ 11 std::vectormá konstruktor seznamu inicializátorů pro svůj typ šablony. Proto tento kód:

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

zavolá konstruktor seznamu inicializátorů, nikoli konstruktor, std::vectorkterý přebírá parametr jedné velikosti a vytvoří vektor s touto velikostí. K přístupu k poslednímu konstruktoru bude uživatel muset použít standardní syntaxi konstruktoru přímo.

Inference typu

V jazyce C ++ 03 (a C) musí být pro použití proměnné její typ výslovně uveden. S příchodem typů šablon a technik metaprogramování šablon nemusí být typ něčeho, zejména dobře definovaná návratová hodnota funkce, snadno vyjádřen. Ukládání meziproduktů do proměnných je tedy obtížné, případně je třeba znalost vnitřních částí dané knihovny metaprogramování.

C ++ 11 umožňuje toto zmírnit dvěma způsoby. Nejprve může autoklíčové slovo použít definice proměnné s explicitní inicializací . Tím se vytvoří proměnná specifického typu inicializátoru:

auto some_strange_callable_type = std::bind(&some_function, _2, _1, some_object);
auto other_variable = 5;

Typ some_strange_callable_typeje prostě jakýkoli, který konkrétní funkce šablony přepíše std::bindvýnosy pro tyto konkrétní argumenty. Tento typ kompilátor snadno stanoví procedurálně jako součást svých sémantických analytických povinností, ale není pro uživatele snadné jej určit po inspekci. Typ other_variableje také dobře definován, ale je pro uživatele jednodušší určit. Je to an int, což je stejný typ jako celočíselný literál.

Toto použití klíčového slova autov C ++ přehodnocuje sémantiku tohoto klíčového slova, které bylo původně použito v beztypovém předchůdci jazyka B v související roli označující netypovanou automatickou definici proměnné .

Klíčové slovo decltypelze dále použít k určení typu výrazu v době kompilace. Například:

int some_int;
decltype(some_int) other_integer_variable = 5;

To je užitečnější ve spojení s auto, protože typ automatické proměnné je znám pouze kompilátoru. Nicméně, decltypemůže být velmi užitečné pro výrazy v kódu, který dělá těžké použití přetěžování operátorů a specializovaných typů.

autoje také užitečné pro snížení výřečnosti kódu. Například místo psaní

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

programátor může použít kratší

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

které lze dále zhutnit, protože „myvec“ implementuje iterátory začátek/konec:

for (const auto& x : myvec)

Tento rozdíl roste, když programátor začíná vnořovat kontejnery, ačkoli v takových případech typedefjsou s dobrým způsobem, jak snížit množství kódu.

Typ označený decltypemůže být odlišný od typu odvozeného auto.

#include <vector>
int main()
{
    const std::vector<int> v(1);
    auto a = v[0];        // a has type int
    decltype(v[0]) b = 1; // b has type const int&, the return type of
                          //   std::vector<int>::operator[](size_type) const
    auto c = 0;           // c has type int
    auto d = c;           // d has type int
    decltype(c) e;        // e has type int, the type of the entity named by c
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue
    decltype(0) g;        // g has type int, because 0 is an rvalue
}

Rozsah založený na smyčce

C ++ 11 rozšiřuje syntaxi forpříkazu, aby umožňoval snadnou iteraci v celé řadě prvků:

int my_array[5] = {1, 2, 3, 4, 5};
// double the value of each element in my_array:
for (int& x : my_array)
    x *= 2;

// similar but also using type inference for array elements
for (auto& x : my_array)
    x *= 2;

Tato forma for, nazývaná „na základě rozsahu“, bude iterovat každý prvek v seznamu. Bude fungovat pro pole ve stylu C, seznamy inicializátorů a jakýkoli typ, který má begin()a end()pro něj definované funkce, které vracejí iterátory. Všechny standardní kontejnery knihovny, které mají dvojice začátek/konec, budou fungovat s příkazem založeným na rozsahu pro.

Lambda funkce a výrazy

C ++ 11 poskytuje možnost vytvářet anonymní funkce , nazývané funkce lambda. Ty jsou definovány následovně:

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

Návratový typ ( -> intv tomto případě) lze vynechat, pokud všechny returnvýrazy vrací stejný typ. Lambda může být volitelně uzávěr .

Syntaxe alternativní funkce

Standardní syntaxe deklarace funkce C byla naprosto dostačující pro sadu funkcí jazyka C. Jak se C ++ vyvinulo z C, zachovalo základní syntaxi a rozšířilo ji, kde bylo potřeba. Jak ale C ++ rostl složitěji, odhalil několik omezení, zejména pokud jde o deklarace funkcí šablony. Například v C ++ 03 je toto zakázáno:

template<class Lhs, class Rhs>
  Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret must be the type of lhs+rhs

Typ Retje jakýkoli přidáním typů Lhsa Rhsbude produkovat. I s výše uvedenou funkcí C ++ 11 decltypeto není možné:

template<class Lhs, class Rhs>
  decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Not valid C++11

Toto není platný C ++, protože lhsa rhsdosud nebyly definovány; nebudou platnými identifikátory, dokud analyzátor nerozebere zbytek prototypu funkce.

Chcete-li to vyřešit, C ++ 11 zavedl novou syntaxi deklarace funkcí s typem koncového návratu :

template<class Lhs, class Rhs>
  auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

Tuto syntaxi lze použít pro běžnější deklarace a definice funkcí:

struct SomeStruct
{
    auto func_name(int x, int y) -> int;
};

auto SomeStruct::func_name(int x, int y) -> int
{
    return x + y;
}

Použití klíčového slova „auto“ v tomto případě je pouze součástí syntaxe a neprovádí automatické odečtení typu v C ++ 11. Počínaje C ++ 14 však lze koncový návratový typ zcela odebrat a kompilátor automaticky odvodí návratový typ.

Vylepšení stavby objektu

V C ++ 03 konstruktéři třídy nesmějí volat jiné konstruktory v seznamu inicializátorů této třídy. Každý konstruktor musí postavit všechny členy třídy sám nebo zavolat společnou členskou funkci následujícím způsobem:

class SomeType
{
public:
    SomeType(int new_number)
    {
        Construct(new_number);
    }

    SomeType()
    {
        Construct(42);
    }

private:
    void Construct(int new_number)
    {
        number = new_number;
    }

    int number;
};

Konstruktory pro základní třídy nelze přímo vystavit odvozeným třídám; každá odvozená třída musí implementovat konstruktory, i když by byl vhodný konstruktor základní třídy. Nestálé datové členy tříd nelze inicializovat na místě deklarace těchto členů. Lze je inicializovat pouze v konstruktoru.

C ++ 11 poskytuje řešení všech těchto problémů.

C ++ 11 umožňuje konstruktérům volat jiné partnerské konstruktory (nazývané delegování ). To umožňuje konstruktérům využívat chování jiného konstruktora s minimem přidaného kódu. Delegace byla použita v jiných jazycích, např. Java a Objective-C .

Tato syntaxe je následující:

class SomeType
{
    int number;

public:
    SomeType(int new_number) : number(new_number) {}
    SomeType() : SomeType(42) {}
};

Všimněte si, že v tomto případě by stejného účinku bylo možné dosáhnout vytvořením new_numbervýchozího parametru. Nová syntaxe však umožňuje vyjádření výchozí hodnoty (42) v implementaci, nikoli v rozhraní - výhoda pro správce kódu knihovny, protože výchozí hodnoty pro parametry funkce jsou „zapečeny“ pro volání webů, zatímco delegace konstruktoru umožňuje hodnota, která má být změněna bez překompilování kódu pomocí knihovny.

S tím souvisí upozornění: C ++ 03 považuje objekt za konstruovaný, když jeho konstruktor dokončí provádění, ale C ++ 11 považuje objekt postavený, jakmile jakýkoli konstruktor dokončí provádění. Protože bude povoleno spouštění více konstruktorů, bude to znamenat, že každý delegující konstruktor bude spouštět na plně konstruovaném objektu svého vlastního typu. Odvozené konstruktory tříd se spustí po dokončení všech delegací v jejich základních třídách.

U konstruktorů základní třídy C ++ 11 umožňuje třídě určit, že konstruktory základní třídy budou zděděny. Kompilátor C ++ 11 tedy vygeneruje kód k provedení dědičnosti a předávání odvozené třídy do základní třídy. Toto je funkce typu všechno nebo nic: buď jsou předány všechny konstruktory této základní třídy, nebo žádný z nich. Také zděděný konstruktor bude zastíněn, pokud odpovídá podpisu konstruktoru odvozené třídy a existují omezení pro vícenásobnou dědičnost: konstruktory tříd nelze zdědit ze dvou tříd, které používají konstruktory se stejným podpisem .

Syntaxe je následující:

class BaseClass
{
public:
    BaseClass(int value);
};

class DerivedClass : public BaseClass
{
public:
    using BaseClass::BaseClass;
};

Pro inicializaci členů umožňuje C ++ 11 tuto syntaxi:

class SomeClass
{
public:
    SomeClass() {}
    explicit SomeClass(int new_value) : value(new_value) {}

private:
    int value = 5;
};

Jakýkoli konstruktor třídy se inicializuje valuepomocí 5, pokud konstruktor nepřepisuje inicializaci vlastní. Výše uvedený prázdný konstruktor se tedy inicializuje valuepodle stavu definice třídy, ale konstruktor, který převezme int, jej inicializuje na daný parametr.

Může také použít konstruktor nebo jednotnou inicializaci namísto výše uvedené inicializace přiřazení.

Explicitní přepisy a konečné

V jazyce C ++ 03 je možné omylem vytvořit novou virtuální funkci, pokud je určena k přepsání funkce základní třídy. Například:

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int);
};

Předpokládejme, že Derived::some_funcje určen k nahrazení verze základní třídy. Ale místo toho, protože má jiný podpis , vytvoří druhou virtuální funkci. Toto je běžný problém, zvláště když uživatel přejde upravit základní třídu.

C ++ 11 poskytuje syntaxi k vyřešení tohoto problému.

struct Base
{
    virtual void some_func(float);
};

struct Derived : Base
{
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

Tyto overridespeciální identifikátor znamená, že kompilátor bude kontrolovat základní třídu (y), aby zjistili, zda je virtuální funkce s tímto přesným podpisem. A pokud tomu tak není, kompilátor indikuje chybu.

C ++ 11 také přidává možnost zabránit dědění z tříd nebo jednoduše zabránit přepsání metod v odvozených třídách. To se provádí pomocí speciálního identifikátoru final. Například:

struct Base1 final { };

struct Derived1 : Base1 { }; // ill-formed because the class Base1 has been marked final
struct Base2
{
    virtual void f() final;
};

struct Derived2 : Base2
{
    void f(); // ill-formed because the virtual function Base2::f has been marked final
};

V tomto příkladu virtual void f() final;příkaz deklaruje novou virtuální funkci, ale také brání odvozeným třídám v přepsání. Má také ten účinek, že brání odvozeným třídám používat tuto konkrétní kombinaci názvu funkce a parametru.

Všimněte si, že ani overrideani finalji jazyková klíčová slova. Jsou to technicky identifikátory atributů deklarátoru:

  • získávají zvláštní význam jako atributy pouze tehdy, jsou -li použity v těchto konkrétních koncových kontextech (po všech specifikátorech typů, specifikátorech přístupu, deklaracích členů (u typů struct, class a enum) a specifikátorů deklarátorů, ale před inicializací nebo implementací kódu každého deklarátoru čárkou -oddělený seznam deklarátorů);
  • nemění deklarovaný typový podpis a nedeklarují ani nepřepisují žádný nový identifikátor v žádném rozsahu;
  • uznávané a přijímané atributy deklarátoru lze v budoucích verzích C ++ rozšířit (některá rozšíření specifická pro kompilátor již rozpoznávají přidané atributy deklarátoru, poskytovat kompilátoru možnosti generování kódu nebo rady pro optimalizaci nebo generovat přidaná data do kompilovaného kódu určeného pro debuggery, linkery a nasazení zkompilovaného kódu nebo k poskytnutí přidaných atributů zabezpečení specifických pro systém nebo k posílení reflexních schopností za běhu nebo k poskytnutí přidaných vazebných informací pro interoperabilitu s jinými programovacími jazyky a runtime systémy; tato rozšíření mohou mít parametry mezi závorky za identifikátor atributu deklarátoru; pro shodu ANSI by tato rozšíření specifická pro kompilátor měla používat konvenci předpony dvojité podtržítko).
  • V jakémkoli jiném umístění to mohou být platné identifikátory pro nové deklarace (a pozdější použití, pokud jsou přístupné).

Konstanta nulového ukazatele

Pro účely této sekce a této sekce je každý výskyt „ 0“ chápán jako „konstantní výraz, který se vyhodnocuje 0, což je typ int“. Ve skutečnosti může být konstantní výraz jakéhokoli integrálního typu.

Od úsvitu C v roce 1972 má konstanta 0dvojí roli konstantní celé číslo a nulová konstanta ukazatele. Nejednoznačnost vlastní dvojímu významu 0byla řešena v C pomocí makra preprocesoru NULL, které se běžně rozšiřuje na buď ((void*)0)nebo 0. C ++ zakazuje implicitní převod z void *na jiné typy ukazatelů, čímž odstraňuje výhodu přetypování 0na void *. V důsledku toho 0je povolena pouze jako konstanta nulového ukazatele. Toto špatně spolupracuje s přetížením funkcí :

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

Pokud NULLje definován jako 0(což je v C ++ obvykle případ), příkaz foo(NULL);zavolá foo(int), což téměř jistě není to, co programátor zamýšlel, a ne to, co naznačuje povrchní čtení kódu.

C ++ 11 řeší tento vložením nové slovo, aby sloužil jako význačný ukazatel NULL konstanta: nullptr. Je typu nullptr_t, který je implicitně konvertibilní a srovnatelný s jakýmkoli typem ukazatele nebo typu ukazatel na člena. Není implicitně konvertibilní nebo srovnatelný s integrálními typy, s výjimkou bool. Zatímco původní návrh uváděl, že rvalue typu nullptr_tby nemělo být konvertibilní bool, pracovní skupina pro základní jazyk se rozhodla, že taková konverze bude žádoucí, kvůli konzistenci s typy běžných ukazatelů. Navrhované změny znění byly jednomyslně odhlasovány do pracovního dokumentu v červnu 2008. Podobný návrh je předložen i standardní pracovní skupině C.

Z důvodů zpětné kompatibility 0zůstává platnou konstantou nulového ukazatele.

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b is false.
int    i = nullptr;     // error

foo(nullptr);           // calls foo(nullptr_t), not foo(int);
/*
  Note that foo(nullptr_t) will actually call foo(char *) in the example above using an implicit conversion,
  only if no other functions are overloading with compatible pointer types in scope.
  If multiple overloadings exist, the resolution will fail as it is ambiguous,
  unless there is an explicit declaration of foo(nullptr_t).

  In standard types headers for C++11, the nullptr_t  type should be declared as:
      typedef decltype(nullptr) nullptr_t;
  but not as:
      typedef int nullptr_t; // prior versions of C++ which need NULL to be defined as 0
      typedef void *nullptr_t; // ANSI C which defines NULL as ((void*)0)
*/

Silně napsané výčty

V jazyce C ++ 03 nejsou výčty bezpečné pro typ. Jsou to vlastně celá čísla, i když jsou typy výčtu odlišné. To umožňuje srovnání mezi dvěma hodnotami výčtu různých typů výčtu. Jedinou bezpečností, kterou C ++ 03 poskytuje, je, že celé číslo nebo hodnota jednoho typu výčtu se implicitně nepřevádí na jiný typ výčtu. Dále je základní integrální typ definován implementací; kód, který závisí na velikosti výčtu, je tedy nepřenosný. A konečně, hodnoty výčtu jsou omezeny na uzavírající rozsah. Není tedy možné, aby dva oddělené výčty ve stejném rozsahu měly shodná jména členů.

C ++ 11 umožňuje speciální klasifikaci výčtu, která nemá žádný z těchto problémů. To je vyjádřeno pomocí enum class( enum structje také přijato jako synonymum) prohlášení:

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

Tento výčet je typově bezpečný. Hodnoty třídy výčtu nejsou implicitně převedeny na celá čísla. Nelze je tedy porovnávat ani s celými čísly (výraz Enumeration::Val4 == 101udává chybu při kompilaci).

Základní typ tříd výčtu je vždy znám. Výchozí typ je int; toto lze přepsat na jiný integrální typ, jak je vidět na tomto příkladu:

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

S výčty starého stylu jsou hodnoty umístěny do vnějšího rozsahu. S novými výčty v novém stylu jsou umístěny v rozsahu názvu třídy výčtu. Takže ve výše uvedeném příkladu Val1není definováno, ale Enum2::Val1je definováno.

Existuje také přechodná syntaxe, která umožňuje výčty ve starém stylu poskytovat explicitní rozsah a definici základního typu:

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

V tomto případě jsou názvy enumerátorů definovány v oboru (enumeration Enum3::Val1) ( ), ale pro zpětnou kompatibilitu jsou také umístěny v obklopujícím oboru.

Předběžně deklarující výčty jsou také možné v C ++ 11. Dříve typy výčtu nebylo možné dopředu deklarovat, protože velikost výčtu závisí na definici jeho členů. Dokud je velikost výčtu zadána buď implicitně, nebo explicitně, lze jej deklarovat dopředu:

enum Enum1;                      // Invalid in C++03 and C++11; the underlying type cannot be determined.
enum Enum2 : unsigned int;       // Valid in C++11, the underlying type is specified explicitly.
enum class Enum3;                // Valid in C++11, the underlying type is int.
enum class Enum4 : unsigned int; // Valid in C++11.
enum Enum2 : unsigned short;     // Invalid in C++11, because Enum2 was formerly declared with a different underlying type.

Pravoúhlý držák

Analyzátor C ++ 03 >>ve všech případech definuje „ “ jako operátor správného posunu nebo operátor extrakce proudu. U vnořených deklarací šablon má však programátor tendenci zanedbávat umístění mezery mezi dvě pravoúhlé závorky, což způsobuje chybu syntaxe kompilátoru.

C ++ 11 vylepšuje specifikaci analyzátoru, takže více závorek pravého úhlu bude interpretováno jako uzavření seznamu argumentů šablony tam, kde je to rozumné. To lze přepsat použitím závorek kolem výrazů parametrů pomocí binárních operátorů „ >“, „ >=“ nebo „ >>“:

template<bool Test> class SomeType;
std::vector<SomeType<1>2>> x1;  // Interpreted as a std::vector of SomeType<true>,
    // followed by "2 >> x1", which is not valid syntax for a declarator. 1 is true.
std::vector<SomeType<(1>2)>> x1;  // Interpreted as std::vector of SomeType<false>,
    // followed by the declarator "x1", which is valid C++11 syntax. (1>2) is false.

Explicitní operátory převodu

C ++ 98 přidal explicitklíčové slovo jako modifikátor do konstruktorů, aby se zabránilo použití konstruktorů s jedním argumentem jako implicitních operátorů převodu typů. Pro skutečné operátory převodu to však nic neznamená. Například třída inteligentního ukazatele může operator bool()umožnit, aby fungovala více jako primitivní ukazatel: pokud obsahuje tuto konverzi, lze ji testovat pomocí if (smart_ptr_variable)(což by platilo, pokud by ukazatel nebyl null a jinak by byl nepravdivý). To však umožňuje i další, nezamýšlené převody. Protože je C ++ booldefinován jako aritmetický typ, lze jej implicitně převést na integrální nebo dokonce typy s plovoucí desetinnou čárkou, což umožňuje matematické operace, které nejsou určeny uživatelem.

V C ++ 11 explicitlze nyní klíčové slovo použít na operátory převodu. Stejně jako u konstruktorů brání použití těchto funkcí převodu v implicitních převodech. Jazykové kontexty, které specificky potřebují booleovskou hodnotu (podmínky příkazů if a smyček a operandů logických operátorů), se však počítají jako explicitní převody, a proto mohou používat logický operátor převodu.

Tato funkce například čistě řeší problém bezpečného bool .

Aliasy šablon

V C ++ 03 je možné definovat typedef pouze jako synonymum pro jiný typ, včetně synonyma pro specializaci šablony se všemi zadanými argumenty skutečné šablony. Šablonu typedef nelze vytvořit. Například:

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
typedef SomeType<OtherType, Second, 5> TypedefName; // Invalid in C++03

Toto nebude kompilováno.

C ++ 11 přidává tuto schopnost s touto syntaxí:

template <typename First, typename Second, int Third>
class SomeType;

template <typename Second>
using TypedefName = SomeType<OtherType, Second, 5>;

usingSyntaxe může být také použit jako typ aliasingu v jazyce C ++ 11:

typedef void (*FunctionType)(double);       // Old style
using FunctionType = void (*)(double); // New introduced syntax

Neomezené odbory

V jazyce C ++ 03 existují omezení, jaké typy objektů mohou být členy a union. Odbory například nemohou obsahovat žádné objekty, které definují netriviální konstruktor nebo destruktor. C ++ 11 zrušuje některá z těchto omezení.

Pokud má uniončlen netriviální speciální členskou funkci , kompilátor nevygeneruje ekvivalentní členskou funkci pro uniona musí být definován ručně.

Toto je jednoduchý příklad sjednocení povoleného v C ++ 11:

#include <new> // Needed for placement 'new'.

struct Point
{
    Point() {}
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_;
};

union U
{
    int z;
    double w;
    Point p; // Invalid in C++03; valid in C++11.
    U() {} // Due to the Point member, a constructor definition is now needed.
    U(const Point& pt) : p(pt) {} // Construct Point object using initializer list.
    U& operator=(const Point& pt) { new(&p) Point(pt); return *this; } // Assign Point object using placement 'new'.
};

Změny neporuší žádný stávající kód, protože pouze uvolní aktuální pravidla.

Vylepšení základních jazykových funkcí

Tyto funkce umožňují jazyku dělat věci, které byly dříve nemožné, extrémně podrobné nebo potřebné nepřenosné knihovny.

Variadické šablony

V C ++ 11 mohou šablony převzít variabilní počet parametrů šablony. To také umožňuje definici typově bezpečných variadických funkcí .

Nové řetězcové literály

C ++ 03 nabízí dva druhy řetězcových literálů . První druh, obsažený v uvozovkách, vytváří pole typu zakončené nulou const char. Druhý druh, definovaný jako L"", produkuje pole typu zakončené nulou const wchar_t, kde wchar_tje široký znak nedefinované velikosti a sémantiky. Žádný doslovný typ nenabízí podporu pro řetězcové literály s kódováním UTF-8 , UTF-16 nebo jakýmkoli jiným druhem kódování Unicode .

Definice typu charbyla upravena tak, aby výslovně vyjadřovala, že je to alespoň velikost potřebná pro uložení osmibitového kódování UTF-8 a dostatečně velká, aby obsahovala libovolného člena základní znakové sady spuštění kompilátoru. Dříve byl definován pouze jako druhý v samotném standardu C ++, poté se spoléhal na standard C, který zaručoval alespoň 8 bitů.

C ++ 11 podporuje tři kódování Unicode: UTF-8 , UTF-16 a UTF-32 . Spolu s dříve zaznamenanými změnami v definici charC ++ 11 přidává dva nové typy znaků: char16_ta char32_t. Ty jsou určeny k ukládání UTF-16, respektive UTF-32.

Vytváření řetězcových literálů pro každé z těchto kódování lze provést takto:

u8"I'm a UTF-8 string."
u"This is a UTF-16 string."
U"This is a UTF-32 string."

Typ prvního řetězce je obvyklý const char[]. Druhým druhým řetězcem je const char16_t[](předpona malá písmena „u“). Typ třetího řetězce je const char32_t[](předpona „U“ velkými písmeny).

Při vytváření řetězcových literálů Unicode je často užitečné vkládat body kódu Unicode přímo do řetězce. Za tímto účelem C ++ 11 umožňuje tuto syntaxi:

u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \U00002018."

Číslo za \uje šestnáctkové číslo; nepotřebuje obvyklou 0xpředponu. Identifikátor \upředstavuje 16bitový bod kódu Unicode; Chcete-li zadat 32bitový kódový bod, použijte \Ua 32bitové hexadecimální číslo. Lze zadat pouze platné body kódu Unicode. Například kódové body v rozsahu U+D800 – U+DFFF jsou zakázány, protože jsou vyhrazeny pro náhradní páry v kódování UTF-16.

Někdy je také užitečné vyhnout se únikům řetězců ručně, zejména pro použití literálů souborů XML , skriptovacích jazyků nebo regulárních výrazů. C ++ 11 poskytuje doslovný řetězec:

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

V prvním případě je vše mezi "(a a )"součástí řetězce. Znakům "a \není třeba unikat. V druhém případě "delimiter(začíná řetězec a končí pouze při )delimiter"dosažení. Řetězec delimitermůže mít libovolný řetězec až 16 znaků, včetně prázdného řetězce. Tento řetězec nemůže obsahovat mezery, řídící znaky, (, ), nebo \znak. Použití tohoto řetězce oddělovače umožňuje uživateli mít )znaky v nezpracovaných řetězcových literálech. Například R"delimiter((a-z))delimiter"je ekvivalentní "(a-z)".

Doslovné řetězcové literály lze kombinovat s širokým literálem nebo libovolnou předponou literálu Unicode:

u8R"XXX(I'm a "raw UTF-8" string.)XXX"
uR"*(This is a "raw UTF-16" string.)*"
UR"(This is a "raw UTF-32" string.)"

Uživatelem definované literály

C ++ 03 poskytuje řadu literálů. Znaky 12.5jsou doslovné, které překladač vyřeší jako typ doubles hodnotou 12,5. Přidání přípony f, jako v 12.5f, však vytvoří hodnotu typu, floatkterá obsahuje hodnotu 12,5. Modifikátory přípon pro literály jsou opraveny specifikací C ++ a kód C ++ 03 nemůže vytvářet nové modifikátory doslovného formátu.

Naproti tomu C ++ 11 umožňuje uživateli definovat nové druhy doslovných modifikátorů, které budou konstruovat objekty na základě řetězce znaků, které literál upravuje.

Transformace literálů je předefinována do dvou odlišných fází: syrové a vařené. Surový literál je posloupnost znaků nějakého konkrétního typu, zatímco vařený literál je samostatného typu. C ++ doslovný 1234, jako surový doslovné, je tento sled znaků '1', '2', '3', '4'. Jako vařené doslovné, to je celé číslo 1234. C ++ doslovný 0xAv surové formě, je '0', 'x', 'A', zatímco ve vařené formě, že je celé číslo 10.

Literály lze prodloužit v syrové i vařené formě, s výjimkou řetězcových literálů, které lze zpracovat pouze ve vařené formě. Tato výjimka je způsobena skutečností, že řetězce mají předpony, které ovlivňují konkrétní význam a typ příslušných znaků.

Všechny uživatelem definované literály jsou přípony; definování doslovných předpon není možné. _Standardem jsou vyhrazeny všechny přípony začínající libovolným znakem kromě podtržítka ( ). Všechny uživatelem definované literály tedy musí mít přípony začínající podtržítkem ( _).

Uživatelsky definované literály zpracovávající nezpracovanou formu literálu jsou definovány prostřednictvím operátoru doslovného zápisu, který je zapsán jako operator "". Následuje příklad:

OutputType operator "" _mysuffix(const char * literal_string)
{
    // assumes that OutputType has a constructor that takes a const char *
    OutputType ret(literal_string);
    return ret;
}

OutputType some_variable = 1234_mysuffix;
// assumes that OutputType has a get_value() method that returns a double
assert(some_variable.get_value() == 1234.0)

Příkaz přiřazení OutputType some_variable = 1234_mysuffix;spustí kód definovaný uživatelem definovanou doslovnou funkcí. Tato funkce je předávána "1234"jako řetězec ve stylu C, takže má nulový terminátor.

Alternativní mechanismus pro zpracování celých čísel a surových literálů s pohyblivou řádovou čárkou je prostřednictvím variadické šablony :

template<char...> OutputType operator "" _tuffix();

OutputType some_variable = 1234_tuffix;
OutputType another_variable = 2.17_tuffix;

To vytvoří instanci funkce doslovného zpracování jako operator "" _tuffix<'1', '2', '3', '4'>(). V tomto formuláři neexistuje žádný znak null ukončující řetězec. Hlavním účelem je použít constexprklíčové slovo C ++ 11, aby bylo zajištěno, že kompilátor převede doslovný text zcela v době kompilace, za předpokladu, že OutputTypejde o typ konstruovatelný a kopírovatelný pomocí constexpr a funkce doslovného zpracování je constexprfunkce.

U numerických literálů je typ vařeného literálu buď unsigned long longpro integrální literály, nebo long doublepro literály s plovoucí desetinnou čárkou. (Poznámka: Není nutné používat podepsané integrální typy, protože doslovný znak s předponou je analyzován jako výraz obsahující znak jako operátor unární předpony a číslo bez znaménka.) Neexistuje žádný alternativní formulářový formulář:

OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _suffix(long double);

OutputType some_variable = 1234_suffix; // Uses the 'unsigned long long' overload.
OutputType another_variable = 3.1416_suffix; // Uses the 'long double' overload.

V souladu s dříve uvedenými novými řetězcovými předponami se pro řetězcové literály používají tyto:

OutputType operator "" _ssuffix(const char     * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const wchar_t  * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char32_t * string_values, size_t num_chars);

OutputType some_variable =   "1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable = u8"1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable =  L"1234"_ssuffix; // Uses the 'const wchar_t *'  overload.
OutputType some_variable =  u"1234"_ssuffix; // Uses the 'const char16_t *' overload.
OutputType some_variable =  U"1234"_ssuffix; // Uses the 'const char32_t *' overload.

Neexistuje žádný alternativní formulář šablony. Znakové literály jsou definovány podobně.

Paměťový model s více vlákny

C ++ 11 standardizuje podporu pro vícevláknové programování .

Jedná se o dvě části: paměťový model, který umožňuje souběžné soužití více vláken v programu a podporu knihovny pro interakci mezi vlákny. (Viz část tohoto článku o zařízeních pro vytváření vláken .)

Paměťový model definuje, kdy více vláken může přistupovat ke stejnému umístění paměti, a určuje, kdy budou aktualizace jedním vláknem viditelné pro ostatní vlákna.

Místní úložiště vláken

V prostředí s více vlákny je běžné, že každé vlákno má nějaké jedinečné proměnné . K tomu již dochází u místních proměnných funkce, ale u globálních a statických proměnných se to nestává.

Nová doba uložení místního vlákna (kromě stávajícího statického , dynamického a automatického ) je určena specifikátorem úložiště thread_local.

Libovolnému objektu, který by mohl mít trvání statického úložiště (tj. Životnost pokrývající celé spuštění programu), může být místo toho přidělena lokální doba trvání vlákna. Záměrem je, aby jako každou jinou proměnnou statické délky mohl být lokální objekt vlákna inicializován pomocí konstruktoru a zničen pomocí destruktoru.

Explicitně výchozí a odstraněné speciální členské funkce

V C ++ 03 kompilátor poskytuje pro třídy, které je neposkytují pro sebe, výchozí konstruktor, konstruktor kopírování, operátor přiřazení kopií ( operator=) a destruktor. Programátor může tyto výchozí hodnoty přepsat definováním vlastních verzí. C ++ také definuje několik globálních operátorů (například operator new), které fungují na všech třídách, což může programátor přepsat.

Vytváření těchto výchozích hodnot je však velmi malé. Vytvoření třídy ze své podstaty nekopírovatelné například vyžaduje deklaraci konstruktoru soukromé kopie a operátora přiřazení kopií a jejich nedefinování. Pokus o použití těchto funkcí je porušením pravidla jedné definice (ODR). I když není vyžadována diagnostická zpráva, porušení může mít za následek chybu linkeru.

V případě výchozího konstruktoru kompilátor nevygeneruje výchozí konstruktor, pokud je třída definována pomocí libovolných konstruktorů. To je užitečné v mnoha případech, ale také je užitečné mít specializované konstruktory a výchozí nastavení generované kompilátorem.

C ++ 11 umožňuje explicitní výchozí a mazání těchto speciálních členských funkcí. Tento typ například výslovně deklaruje, že používá výchozí konstruktor:

struct SomeType
{
    SomeType() = default; //The default constructor is explicitly stated.
    SomeType(OtherType value);
};

Alternativně lze některé funkce výslovně zakázat. Tento typ například nelze kopírovat:

struct NonCopyable
{
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

= deleteSpecifikátor lze použít k zabránění volání jakékoliv funkce, která může být použita, aby zakázat volání funkce členů s jednotlivých parametrů. Například:

struct NoInt
{
    void f(double i);
    void f(int) = delete;
};

Pokus o volání f()pomocí intbude kompilátorem odmítnut, místo aby provedl tichý převod na double. To lze zobecnit tak, aby bylo zakázáno volání funkce jakýmkoli jiným typem než doublenásledujícím způsobem:

struct OnlyDouble
{
    void f(double d);
    template<class T> void f(T) = delete;
};

Typ long long int

V C ++ 03 je největší celočíselný typ long int. Je zaručeno, že bude mít alespoň tolik použitelných bitů jako int. Výsledkem long intbyla velikost 64 bitů u některých populárních implementací a 32 bitů u jiných. C ++ 11 long long intk řešení tohoto problému přidává nový celočíselný typ . Je zaručeno, že bude alespoň tak velký jako a long inta bude mít minimálně 64 bitů. Typ byl původně zaveden C99 do standardního C a většina kompilátorů C ++ jej již podporovala jako rozšíření.

Statická tvrzení

C ++ 03 poskytuje dvě metody pro testování tvrzení : makro asserta direktivu preprocesoru #error. Žádné z nich však není vhodné pro použití v šablonách: makro testuje tvrzení při spuštění, zatímco směrnice preprocesoru testuje tvrzení během předzpracování, ke kterému dochází před instancí šablon. Ani jedno není vhodné pro testování vlastností, které jsou závislé na parametrech šablony.

Nový nástroj zavádí nový způsob testování tvrzení při kompilaci pomocí nového klíčového slova static_assert. Prohlášení předpokládá tento formulář:

static_assert (constant-expression, error-message);

Zde je několik příkladů, jak static_assertlze použít:

static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
template<class T>
struct Check
{
    static_assert(sizeof(int) <= sizeof(T), "T is not big enough!");
};
template<class Integral>
Integral foo(Integral x, Integral y)
{
    static_assert(std::is_integral<Integral>::value, "foo() parameter must be an integral type.");
}

Když je konstantní výraz, falsekompilátor vytvoří chybovou zprávu. První příklad je podobný direktivě preprocesoru #error, ačkoli preprocesor podporuje pouze integrální typy. Naproti tomu v druhém příkladu je tvrzení kontrolováno při každé instanci třídy šablony Check.

Statická tvrzení jsou užitečná i mimo šablony. Například daná implementace algoritmu může záviset na velikosti long longbytosti větší než intněco, co standard nezaručuje. Takový předpoklad platí pro většinu systémů a překladačů, ale ne pro všechny.

Povolit sizeofpráci na členech tříd bez explicitního objektu

V C ++ 03 lze sizeofoperátor použít na typy a objekty. Nelze to však použít k tomuto:

struct SomeType { OtherType member; };

sizeof(SomeType::member); // Does not work with C++03. Okay with C++11

To by mělo vrátit velikost OtherType. C ++ 03 to zakazuje, takže se jedná o chybu kompilace. C ++ 11 to umožňuje. Je také povoleno pro alignofoperátora zavedeného v C ++ 11.

Zarovnání objektu řízení a dotazu

C ++ 11 umožňuje vyhledávat a řídit variabilní zarovnání pomocí alignofa alignas.

alignofOperátor má typ a vrátí sílu 2 bajtové hranici, na které musí být přiděleny instancí typu (jako std::size_t). Když daný typ odkazu alignofvrátí zarovnání odkazovaného typu; pro pole vrací zarovnání typu prvku.

alignasSpecifier řídí zarovnání paměti pro proměnné. Specifikátor má konstantu nebo typ; při dodání je typ alignas(T)zkratkou alignas(alignof(T)). Chcete -li například určit, že pole char by mělo být správně zarovnáno tak, aby obsahovalo plovák:

alignas(float) unsigned char c[sizeof(float)]

Povolit implementace shromažďované odpadky

Předchozí standardy C ++ poskytované pro programátorem řízené shromažďování odpadků prostřednictvím set_new_handler, ale nedávaly žádnou definici dosažitelnosti objektů za účelem automatického shromažďování odpadků. C ++ 11 definuje podmínky, za kterých jsou hodnoty ukazatelů „bezpečně odvozeny“ z jiných hodnot. Implementace může určit, že funguje pod přísnou bezpečností ukazatele . V takovém případě mohou být ukazatele, která nejsou odvozena podle těchto pravidel, neplatná.

Atributy

C ++ 11 poskytuje standardizovanou syntaxi pro rozšíření překladače/nástroje do jazyka. Taková rozšíření byla tradičně specifikována pomocí #pragmadirektivy nebo klíčových slov specifických pro dodavatele (jako __attribute__pro GNU a __declspecMicrosoft). S novou syntaxí lze přidané informace zadat ve formě atributu uzavřeného do dvojitých hranatých závorek. Atribut lze použít na různé prvky zdrojového kódu:

int [[attr1]] i [[attr2, attr3]];

[[attr4(arg1, arg2)]] if (cond)
{
    [[vendor::attr5]] return i;
}

Ve výše uvedeném příkladu, atribut attr1se vztahuje na typ proměnné i, attr2a attr3platí pro samotnou proměnnou, attr4se vztahuje na ifprohlášení a vendor::attr5platí pro příkazem return. Obecně (ale s některými výjimkami) je atribut určený pro pojmenovanou entitu umístěn za název a před entitu jinak, jak je uvedeno výše, může být v jedné dvojici dvojitých hranatých závorek uvedeno několik atributů, mohou být uvedeny přidané argumenty pro atribut a atributy mohou být vymezeny obory názvů atributů specifických pro dodavatele.

Doporučuje se, aby atributy neměly sémantický význam jazyka a neměly smysl programu, pokud jsou ignorovány. Atributy mohou být užitečné pro poskytování informací, které například pomáhají kompilátoru vydávat lepší diagnostiku nebo optimalizovat generovaný kód.

C ++ 11 sám poskytuje dva standardní atributy: noreturnurčit, že se funkce nevrací, a carries_dependencypomoci optimalizovat vícevláknový kód uvedením, že argumenty funkce nebo návratová hodnota nesou závislost.

Změny standardní knihovny C ++

Ve standardní knihovně C ++ 11 byla zavedena řada nových funkcí. Mnoho z nich mohlo být implementováno podle starého standardu, ale některé spoléhají (ve větší či menší míře) na nové základní funkce C ++ 11.

Velká část nových knihoven byla definována v dokumentu C ++ Standards Committee's Library Technical Report (nazvaný TR1), který byl publikován v roce 2005. V současné době jsou k dispozici různé jmenné oblasti TR1 a jejich plné využití std::tr1. Pro C ++ 11 byly přesunuty do oboru názvů std. Protože však funkce TR1 byly přeneseny do standardní knihovny C ++ 11, byly případně upgradovány o funkce jazyka C ++ 11, které nebyly k dispozici v původní verzi TR1. Také mohou být vylepšeny o funkce, které byly možné v C ++ 03, ale nebyly součástí původní specifikace TR1.

Upgrady na standardní součásti knihovny

C ++ 11 nabízí řadu nových jazykových funkcí, z nichž mohou těžit aktuálně existující standardní součásti knihovny. Většina standardních knihovních kontejnerů může například těžit z podpory konstruktoru přesunu založeného na referencích Rvalue, a to jak pro rychlý přesun těžkých kontejnerů, tak pro přesun obsahu těchto kontejnerů do nových paměťových míst. Standardní součásti knihovny byly v případě potřeby upgradovány o nové funkce jazyka C ++ 11. Mezi ně patří, ale nejsou omezeny na:

  • Rvalue reference a související podpora přesunu
  • Podpora pro kódovací jednotky UTF-16 a Unicode pro kódovací jednotky UTF-32
  • Variadické šablony (spolu s referencemi Rvalue umožňující dokonalé přeposílání)
  • Časově konstantní výrazy
  • decltype
  • explicit operátory převodu
  • Funkce deklarované jako výchozí nebo odstraněné

Od předchozího standardu C ++ již uplynulo mnoho času. Byla napsána velká část kódu pomocí standardní knihovny. To odhalilo části standardních knihoven, které by mohly využít určité vylepšení. Mezi mnoha zvažovanými oblastmi zlepšení byly standardní alokátory knihoven . Nový model alokátorů založený na rozsahu byl zahrnut v C ++ 11 k doplnění předchozího modelu.

Navlékací zařízení

Zatímco jazyk C ++ 03 poskytuje paměťový model, který podporuje vytváření vláken, primární podpora pro skutečné používání vláken je dodávána se standardní knihovnou C ++ 11.

K dispozici je třída vlákna ( std::thread), která v novém vlákně spustí objekt funkce (a volitelnou sérii argumentů, které mu mají být předány). Je možné způsobit, že se vlákno zastaví, dokud se nedokončí jiné provádějící vlákno a poskytne podporu spojování vláken prostřednictvím std::thread::join()členské funkce. Pokud je to možné, přístup je k základním objektům nativního vlákna pro operace specifické pro platformu poskytován std::thread::native_handle()členskou funkcí.

Pro synchronizaci mezi vlákny jsou do knihovny přidány příslušné mutexy ( std::mutex, std::recursive_mutexatd.) A proměnné podmínek ( std::condition_variablea std::condition_variable_any). Ty jsou přístupné prostřednictvím zámků Resource Acquisition Is Initialization (RAII) ( std::lock_guarda std::unique_lock) a uzamykacích algoritmů pro snadné použití.

Pro vysoce výkonnou práci na nízké úrovni je někdy potřeba komunikace mezi vlákny bez režie mutexů. To se provádí pomocí atomových operací na paměťových místech. Ty mohou volitelně určit minimální omezení viditelnosti paměti potřebná pro operaci. K tomuto účelu lze také použít explicitní paměťové bariéry .

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

Další zařízení pro vytváření vláken na vysoké úrovni, jako jsou fondy vláken, byla vrácena do budoucí technické zprávy C ++ . Nejsou součástí C ++ 11, ale očekává se, že jejich případná implementace bude postavena zcela na funkcích knihovny vláken.

Nové std::asynczařízení poskytuje pohodlný způsob spouštění úkolů a jejich vázání na a std::future. Uživatel si může vybrat, zda má být úkol spuštěn asynchronně na samostatném vlákně nebo synchronně na vlákně, které čeká na hodnotu. Ve výchozím nastavení si může implementace vybrat, což poskytuje snadný způsob, jak využít výhod souběžnosti hardwaru bez předplatného, ​​a poskytuje některé výhody fondu vláken pro jednoduchá použití.

Typy řazené kolekce členů

Tuple jsou kolekce složené z heterogenních objektů předem uspořádaných dimenzí. Tuple lze považovat za zobecnění členských proměnných struktury.

Verze C ++ 11 typu n -tice TR1 těžila z funkcí C ++ 11, jako jsou variadické šablony . Aby byla implementována rozumně, verze TR1 vyžadovala implementací definovaný maximální počet obsažených typů a značný podvod s makry. Naproti tomu implementace verze C ++ 11 nevyžaduje žádný explicitní implementací definovaný maximální počet typů. Ačkoli kompilátory budou mít vnitřní maximální hloubku rekurze pro vytváření instancí šablon (což je normální), verze n -tic C ++ 11 tuto hodnotu uživateli nevystaví.

Pomocí variadických šablon vypadá deklarace třídy řazené kolekce členů takto:

template <class ...Types> class tuple;

Příklad definice a použití typu řazené kolekce členů:

typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");

lengthy = std::get<0>(proof);  // Assign to 'lengthy' the value 18.
std::get<3>(proof) = " Beautiful!";  // Modify the tuple’s fourth element.

Je možné vytvořit řazenou kolekci členů proofbez definování jejího obsahu, ale pouze v případě, že typy prvků řazené kolekce členů mají výchozí konstruktory. Navíc je možné přiřadit řazené kolekce členů k jiné řazené kolekci členů: pokud jsou dva typy řazených kolekcí stejné, každý typ prvku musí mít konstruktor kopírování; v opačném případě musí být každý typ prvku n-tice na pravé straně konvertovatelný na typ příslušného typu prvku řazené kolekce členů na levé straně nebo na odpovídající typ prvku kolekce řazených prvků na levé straně vhodný konstruktor.

typedef std::tuple <int , double, string       > tuple_1 t1;
typedef std::tuple <char, short , const char * > tuple_2 t2 ('X', 2, "Hola!");
t1 = t2; // Ok, first two elements can be converted,
         // the third one can be constructed from a 'const char *'.

Stejně jako std::make_pairpro std::pair, existuje std::make_tupleautomatické vytváření std::tuples pomocí dedukce typu a autopomáhá deklarovat takovou n -tici. std::tievytváří n -tice odkazů na hodnotu lvalue, které vám pomohou rozbalit n -tice. std::ignorezde také pomáhá. Viz příklad:

auto record = std::make_tuple("Hari Ram", "New Delhi", 3.5, 'A');
std::string name ; float gpa ; char grade ;
std::tie(name, std::ignore, gpa, grade) = record ; // std::ignore helps drop the place name
std::cout << name << ' ' << gpa << ' ' << grade << std::endl ;

K dispozici jsou relační operátory (mezi n -ticemi se stejným počtem prvků) a dva výrazy pro kontrolu vlastností řazené kolekce členů (pouze během kompilace):

  • std::tuple_size<T>::valuevrací počet prvků v řazené kolekci členů T,
  • std::tuple_element<I, T>::typevrací typ čísla objektu Iřazené kolekce členů T.

Hashovací tabulky

Zahrnutí hashovacích tabulek (neuspořádaných asociativních kontejnerů) do standardní knihovny C ++ je jedním z nejčastěji se opakujících požadavků. Nebyl přijat v C ++ 03 pouze z časových důvodů. Ačkoli jsou hashovací tabulky v nejhorším případě méně účinné než vyvážený strom ( v případě mnoha kolizí), v mnoha reálných aplikacích fungují lépe.

Kolize jsou řízeny pouze pomocí lineárního řetězení, protože výbor nepovažoval za vhodné standardizovat řešení otevřeného adresování, která přinášejí spoustu vnitřních problémů (především když je povoleno mazání prvků). Aby se zabránilo střetům názvů s nestandardními knihovnami, které vyvíjely vlastní implementace hashovací tabulky, byla místo „hash“ použita předpona „unordered“.

Nová knihovna má čtyři typy hashovacích tabulek, rozlišené podle toho, zda přijímají prvky se stejným klíčem (jedinečné klíče nebo ekvivalentní klíče), a podle toho, zda mapují každý klíč na přidruženou hodnotu. Odpovídají čtyřem existujícím asociativním kontejnerům založeným na binárním vyhledávacím stromu s předponou unordered_ .

Typ tabulky hash Přidružené hodnoty Ekvivalentní klíče
std::unordered_set Ne Ne
std::unordered_multiset Ne Ano
std::unordered_map Ano Ne
std::unordered_multimap Ano Ano

Nové třídy splňují všechny požadavky na třídu kontejneru , a mají všechny metody potřebné pro přístup k prvkům: insert, erase, begin, end.

Tato nová funkce nepotřebovala žádná základní rozšíření jazyka C ++ (ačkoli implementace budou využívat výhod různých funkcí jazyka C ++ 11), pouze malé rozšíření záhlaví <functional>a zavedení záhlaví <unordered_set>a <unordered_map>. Nebyly zapotřebí žádné další změny stávajících standardních tříd a nezávisí to na žádných dalších rozšířeních standardní knihovny.

Regulární výrazy

Nová knihovna definovaná v novém záhlaví <regex>je vytvořena z několika nových tříd:

  • regulární výrazy jsou reprezentovány instancí třídy template std::regex;
  • výskyty jsou reprezentovány instancí třídy šablony std::match_results.

Funkce std::regex_searchse používá pro vyhledávání, zatímco pro 'hledat a nahradit' std::regex_replacese používá funkce, která vrací nový řetězec. Algoritmy std::regex_searcha std::regex_replacevezmou regulární výraz a řetězec a zapíší výskyty nalezené ve struktuře std::match_results.

Zde je příklad použití std::match_results:

const char *reg_esp = "[ ,.\\t\\n;:]"; // List of separator characters.

// this can be done using raw string literals:
// const char *reg_esp = R"([ ,.\t\n;:])";

std::regex rgx(reg_esp); // 'regex' is an instance of the template class
                         // 'basic_regex' with argument of type 'char'.
std::cmatch match; // 'cmatch' is an instance of the template class
                   // 'match_results' with argument of type 'const char *'.
const char *target = "Unseen University - Ankh-Morpork";

// Identifies all words of 'target' separated by characters of 'reg_esp'.
if (std::regex_search(target, match, rgx))
{
    // If words separated by specified characters are present.

    const size_t n = match.size();
    for (size_t a = 0; a < n; a++)
    {
        std::string str (match[a].first, match[a].second);
        std::cout << str << "\n";
    }
}

Všimněte si použití dvojitých zpětných lomítek , protože C ++ používá zpětné lomítko jako únikový znak. K zamezení problému lze použít funkci C ++ 11 raw string .

Knihovna <regex>nevyžaduje ani úpravu stávajícího záhlaví (i když je bude případně používat) ani rozšíření základního jazyka. V POSIX C jsou regulární výrazy k dispozici také v knihovně C POSIX#regex.h .

Univerzální inteligentní ukazatele

C ++ 11 poskytuje std::unique_ptra vylepšení do std::shared_ptra std::weak_ptrz TR1. std::auto_ptrje zastaralý.

Rozšiřitelné zařízení náhodných čísel

Standardní knihovna C poskytuje možnost generovat pseudonáhodná čísla prostřednictvím funkce rand. Algoritmus je však zcela delegován na dodavatele knihovny. C ++ zdědil tuto funkci beze změn, ale C ++ 11 poskytuje novou metodu pro generování pseudonáhodných čísel.

Funkce náhodných čísel C ++ 11 je rozdělena na dvě části: generátorový motor, který obsahuje stav generátoru náhodných čísel a vytváří pseudonáhodná čísla; a rozdělení, které určuje rozsah a matematické rozdělení výsledku. Tyto dva jsou spojeny a tvoří objekt generátoru náhodných čísel.

Na rozdíl od standardu randC bude mechanismus C ++ 11 dodáván se třemi algoritmy motoru základního generátoru:

C ++ 11 také poskytuje řadu standardních distribucí:

Generátor a distribuce jsou kombinovány jako v tomto příkladu:

#include <random>
#include <functional>

std::uniform_int_distribution<int> distribution(0, 99);
std::mt19937 engine; // Mersenne twister MT19937
auto generator = std::bind(distribution, engine);
int random = generator(); // Generate a uniform integral variate between 0 and 99.
int random2 = distribution(engine); // Generate another sample directly using the distribution and the engine objects.

Reference obalu

Odkaz na obal je získán z instance třídy šablony reference_wrapper. Odkazy na obálky jsou podobné normálním odkazům (' &') jazyka C ++. K získání odkazu na obálku z jakéhokoli objektu refse používá šablona funkce (pro konstantní odkaz crefse používá).

Odkazy na obálky jsou užitečné především pro šablony funkcí, kde jsou potřeba odkazy na parametry, nikoli na kopie:

// This function will obtain a reference to the parameter 'r' and increment it.
void func (int &r)  { r++; }

// Template function.
template<class F, class P> void g (F f, P t)  { f(t); }

int main()
{
    int i = 0;
    g (func, i); // 'g<void (int &r), int>' is instantiated
                 // then 'i' will not be modified.
    std::cout << i << std::endl; // Output -> 0

    g (func, std::ref(i)); // 'g<void(int &r),reference_wrapper<int>>' is instantiated
                           // then 'i' will be modified.
    std::cout << i << std::endl; // Output -> 1
}

Tento nový nástroj byl přidán do stávajícího <utility>záhlaví a nepotřeboval další rozšíření jazyka C ++.

Polymorfní obaly pro funkční objekty

Polymorfní obaly pro funkční objekty jsou podobné ukazatelům funkcí v sémantice a syntaxi, ale jsou méně pevně svázány a mohou bez rozdílu odkazovat na cokoli, co lze volat (ukazatele funkcí, ukazatele na členské funkce nebo funktory), jejichž argumenty jsou kompatibilní s argumenty obálky .

Jeho vlastnosti může objasnit příklad:

std::function<int (int, int)> func; // Wrapper creation using
                                    // template class 'function'.
std::plus<int> add; // 'plus' is declared as 'template<class T> T plus( T, T ) ;'
                    // then 'add' is type 'int add( int x, int y )'.
func = add;  // OK - Parameters and return types are the same.

int a = func (1, 2); // NOTE: if the wrapper 'func' does not refer to any function,
                     // the exception 'std::bad_function_call' is thrown.

std::function<bool (short, short)> func2 ;
if (!func2)
{
    // True because 'func2' has not yet been assigned a function.

    bool adjacent(long x, long y);
    func2 = &adjacent; // OK - Parameters and return types are convertible.

    struct Test
    {
        bool operator()(short x, short y);
    };
    Test car;
    func = std::ref(car); // 'std::ref' is a template function that returns the wrapper
                          // of member function 'operator()' of struct 'car'.
}
func = func2; // OK - Parameters and return types are convertible.

Třída šablony functionbyla definována uvnitř záhlaví <functional>, aniž by bylo nutné měnit jazyk C ++.

Typové vlastnosti pro metaprogramování

Metaprogramování spočívá ve vytvoření programu, který vytvoří nebo upraví jiný program (nebo sám). To se může stát během kompilace nebo během provádění. ++ Standards výbor C se rozhodla zavést knihovnu, která umožňuje metaprogramming při sestavování pomocí šablon.

Zde je příklad metaprogramu používajícího standard C ++ 03: rekurze instancí šablon pro výpočet celočíselných exponentů:

template<int B, int N>
struct Pow
{
    // recursive call and recombination.
    enum{ value = B*Pow<B, N-1>::value };
};

template< int B >
struct Pow<B, 0>
{
    // ''N == 0'' condition of termination.
    enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;

Mnoho algoritmů může fungovat na různých typech dat; Šablony C ++ podporují generické programování a činí kód kompaktnějším a užitečnějším. Přesto je běžné, že algoritmy vyžadují informace o používaných typech dat. Tyto informace lze extrahovat během instance třídy šablony pomocí vlastností typu .

Vlastnosti typu mohou identifikovat kategorii objektu a všechny charakteristiky třídy (nebo struktury). Jsou definovány v novém záhlaví <type_traits>.

V dalším příkladu je funkce šablony „propracovat“, která v závislosti na daných datových typech vytvoří instanci jednoho ze dvou navrhovaných algoritmů ( algorithm.do_it).

// First way of operating.
template< bool B > struct Algorithm
{
    template<class T1, class T2> static int do_it (T1 &, T2 &)  { /*...*/ }
};

// Second way of operating.
template<> struct Algorithm<true>
{
    template<class T1, class T2> static int do_it (T1, T2)  { /*...*/ }
};

// Instantiating 'elaborate' will automatically instantiate the correct way to operate.
template<class T1, class T2>
int elaborate (T1 A, T2 B)
{
    // Use the second way only if 'T1' is an integer and if 'T2' is
    // in floating point, otherwise use the first way.
    return Algorithm<std::is_integral<T1>::value && std::is_floating_point<T2>::value>::do_it( A, B ) ;
}

Prostřednictvím vlastností typu definovaných v záhlaví <type_traits>je také možné vytvářet operace transformace typů ( static_casta const_castjsou nedostatečné uvnitř šablony).

Tento typ programování vytváří elegantní a výstižný kód; slabou stránkou těchto technik je však ladění: nepohodlné během kompilace a velmi obtížné během provádění programu.

Jednotná metoda pro výpočet návratového typu funkčních objektů

Určení návratového typu objektu funkce šablony v době kompilace není intuitivní, zvláště pokud návratová hodnota závisí na parametrech funkce. Jako příklad:

struct Clear
{
    int    operator()(int) const;    // The parameter type is
    double operator()(double) const; // equal to the return type.
};

template <class Obj>
class Calculus
{
public:
    template<class Arg> Arg operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

Instance instance šablony třídy Calculus<Clear>, objekt funkce calculusbude mít vždy stejný návratový typ jako objekt funkce Clear. Nicméně, vzhledem k Confusedníže uvedené třídě :

struct Confused
{
    double operator()(int) const;     // The parameter type is not
    int    operator()(double) const;  // equal to the return type.
};

Pokus o vytvoření instance Calculus<Confused>způsobí, že návratový typ Calculusnebude stejný jako u třídy Confused. Kompilátor může generovat varování o převodu z intdo doublea naopak.

TR1 zavádí a C ++ 11 přijímá třídu šablony, std::result_ofkterá umožňuje určit a použít návratový typ objektu funkce pro každou deklaraci. Objekt CalculusVer2používá std::result_ofobjekt k odvození návratového typu objektu funkce:

template< class Obj >
class CalculusVer2
{
public:
    template<class Arg>
    typename std::result_of<Obj(Arg)>::type operator()(Arg& a) const
    {
        return member(a);
    }
private:
    Obj member;
};

Tímto způsobem v případech funkčního objektu CalculusVer2<Confused>neexistují žádné převody, varování nebo chyby.

Jedinou změnou oproti verzi TR1 std::result_ofje, že verze TR1 umožnila selhání implementace, aby mohla určit typ výsledku volání funkce. Kvůli změnám v C ++ pro podporu decltypeverze C ++ 11 std::result_ofjiž tyto speciální případy nepotřebuje; implementace jsou nutné k výpočtu typu ve všech případech.

Vylepšená kompatibilita C.

Pro kompatibilitu s C , od C99, byly přidány tyto:

  • Předprocesor:
    • variadická makra ,
    • zřetězení sousedních úzkých/širokých řetězcových literálů,
    • _Pragma()- ekvivalent #pragma.
  • long long - celočíselný typ, který je dlouhý alespoň 64 bitů.
  • __func__ - makro vyhodnocující název funkce, ve které je.
  • Záhlaví:
    • cstdbool( stdbool.h),
    • cstdint( stdint.h),
    • cinttypes( inttypes.h).

Funkce původně plánované, ale odebrané nebo nezahrnuté

Okruh pro samostatný TR:

  • Moduly
  • Desetinné typy
  • Matematické speciální funkce

Odloženo:

  • Pojmy
  • Úplnější nebo požadovaná podpora shromažďování odpadků
  • Odraz
  • Makroobory

Funkce odstraněny nebo zastaralé

Termín sekvenční bod byl odstraněn, přičemž byl nahrazen zadáním, že buď jedna operace je sekvenována před druhou, nebo že dvě operace jsou bez následků.

Dřívější použití klíčového slova exportbylo odstraněno. Klíčové slovo samotné zůstává vyhrazeno pro potenciální budoucí použití.

Specifikace dynamických výjimek jsou zastaralé. S noexceptklíčovým slovem, které je užitečné pro optimalizaci, je k dispozici specifikace v čase kompilace funkcí , které nevyvolávají výjimky .

std::auto_ptrje zastaralý, protože byl nahrazen std::unique_ptr.

Základní třídy funkčních objektů ( std::unary_function, std::binary_function), adaptéry na ukazatele na funkce a adaptéry na ukazatele na členy a třídy pořadače jsou zastaralé.

Viz také

Reference

externí odkazy