C++11

standard programovacího jazyka C++

C++11 (dříve C++0x[1]) je standard programovacího jazyka C++. Byl schválen organizací ISO dne 12. srpna 2011 a nahrazuje C++03. Označení navazuje na tradici pojmenovávání verzí jazyka podle roku publikování specifikace. C++11 přidává několik prvků k jádru jazyka a rozšiřuje standardní C++ knihovnu. V září 2011 byl standard publikován jako ISO/IEC 14882:2011.[2]

Verze jazyka C++
Autor jazyka C++ Bjarne Stroustrup (vlevo) na konferenci CEE-SECR 2010 v Moskvě

Nejnovějšími standardy jsou postupně C++14, C++17 a C++20, a pracuje se na standardu C++23.[3][4]

Cíle návrhu

editovat

Komise pro návrh C++11 kladla důraz na:

  • zachování stability a kompatibility se staršími verzemi C++ a jazykem C
  • nerozšiřování jádra jazyka (pokud je to možné) – nové vlastnosti přednostně zavádět do standardní knihovny[5]
  • zlepšení jazyka C++, aby se usnadnil návrh systémů a knihoven místo zavádění nových vlastností užitečných pouze pro určité aplikace
  • zvýšení typové bezpečnosti poskytnutím bezpečnějších alternativ k původním nebezpečným technikám
  • zvýšení výkonnosti a zlepšení schopností pracovat přímo s hardwarem
  • poskytování vhodných nástrojů pro řešení reálných problémů
  • usnadnění učení a vyučování jazyku C++ bez odstraňování užitečných vlastností používaných zkušenými programátory

Významná je pozornost věnovaná začátečníkům, protože velká část programátorů jimi navždy zůstane, a protože si mnoho začátečníků nikdy nerozšíří své znalosti nad rámec těch aspektů jazyka, na které se specializují.[1][5]

Rozšíření jádra jazyka C++

editovat

Jednou z funkcí komise C++ je vývoj jádra jazyka. K oblastem jádra jazyka, které byly výrazně vylepšeny, patří podpora multithreadingu, podpora generického programování, uniformní inicializace a zvyšování výkonnosti.

Rozšíření jádra pro rychlejší běh

editovat

Reference jako r-hodnota a move konstruktor

editovat

Umožňuje použít referenci na objekt jako r-hodnotu. R-hodnota je dočasná konstanta, do které nelze nijak zapisovat.

int a;
a = 5 + 10 // (5 + 10) je r-hodnota
(5 + 10) = a; // nelze přiřadit hodnotu r-hodnotě.

int& b = 10; // nelze, nemůže existovat nekonstantní reference na r-hodnotu, konstanta není uložená v paměti, ale dosazena překladačem
const int& b = 10; // lze, za b se vždy dosadí 10
int&& b = 10; // lze, uloží se do paměti jako dočasná proměnná, inicializuje se na hodnotu 10

Move konstruktor lze využít pro přesun zdrojů z jedné třídy do druhé bez alokace a dealokace paměti. C++11 zavádí operátor && jako referenci na r-hodnotu. Move konstruktor je definován jako Trida(Trida&& trida), a zavolá se při konstrukci objektu z reference na objekt, který je r-hodnotou.

class Array {
    int* pole;
    size_t s;

public:
    // Konstruktor
    Array(size_t s) : s(s) {
        pole = new int[s];
    }

    // Kopírovací konstruktor
    Array(const Tr1& par) {
        pole = new int[s];
        memcpy(pole, par.pole, sizeof(int) * s);
    }

    // Move konstruktor
    Array(Tr1&& par) {
        pole = par.pole;
        s = par.s;
        par.pole = 0;
        par.s = 0;
    }

    ~Array() {
        delete[] pole;
    }
};

Array pole1(100); // Zavolá standardní konstruktor
Array pole2(static_cast<Array&&>(pole1)); // Přetypuje pole1 na referenci na r-hodnotu, a zavolá move konstruktor
// pole2 je stejné jako bylo pole1 a pole1 je prázdné

constexpr – Obecné konstantní výrazy

editovat

C++11 zavádí klíčové slovo constexpr, které u funkce nebo objektu vynutí, že bude vyhodnocena (objekt konstruován) v době kompilace.

int get_five() {
    return 5;
}

int some_value[get_five() + 7]; // Nelze použít, get_five() není konstantní výraz

enum {
    five = get_five(); // Nelze použít, get_five() není konstantní vyraz
};

constexpr int get_five() {
    return 5;
}

int some_value[get_five() + 7];

enum {
    five = get_five();
};

Nová definice POD

editovat

Třída nebo struktura v C++03 musí splňovat několik podmínek, aby byla považována za plain old data (POD) typ. Typy, které této definici vyhovují, vytvářejí rozvržení objektů, které je kompatibilní s jazykem C, a je možné je inicializovat staticky. Norma C++03 má omezení na to, jaké typy jsou kompatibilní s jazykem C a které lze staticky inicializovat, protože neexistuje žádný technický důvod, aby překladač nemohl program přijmout; pokud by někdo úmyslně vytvořil C++03 POD typ a přidal nevirtuální členskou funkci, tento typ by již nebyl POD typem, nemohl by být staticky inicializován, a nebyl by kompatibilní s jazykem C, i kdyby nedošlo ke změně rozložení paměti.

C++11 uvolnilo několik pravidel pro POD a rozdělilo koncept POD na dva samostatné koncepty: triviální a se standardní strukturou.

Typ, který je triviální, může být staticky inicializován. To znamená, že jeho obsah lze kopírovat funkcí memcpy a není nutné používat kopírovací konstruktor. Životnost triviálního typu začíná, když je definován jeho úložný prostor, a ne když končí konstruktor.

Třída nebo struktura je triviální, pokud:

  1. má triviální implicitní konstruktor; konstruktor může používat implicitní konstruktoru syntax (SomeConstructor() = default;).
  2. má triviální kopírovací a přesunové konstruktory, který může použít implicitní syntax.
  3. má triviální kopírovací a přesunové operátory přiřazení, které mohou použít implicitní syntax.
  4. má triviální destruktor, který nesmí být virtuální.

Konstruktory jsou triviální, pouze pokud třída nemá žádné virtuální členské funkce a žádné virtuální základní třídy. Operace kopírování a přesunu také vyžadují, aby všechny nestatické datové členy byly triviální.

Že typ má standardní strukturu, znamená, že uspořádává a pakuje své členy způsobem, které je kompatibilní s jazykem C. Třída nebo struktura je standardní strukturou, pokud:

  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 (public, private, protected)
  4. všechno jeho nestatické datové členy, včetně libovolný v jeho základní třídy, jsou v stejný jeden třída v hierarchie
  5. výše uvedená pravidla také platí pro všechny bázové třídy a 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 nebo union se považuje za POD, pokud je triviální, má standardní strukturu, a veškeré jeho nestatické datové členy a základní třídy jsou PODy.

Díky oddělení těchto konceptů je možné se jednoho vzdát, aniž by byl porušen druhý. Třída se složitými přesunovými a kopírovací konstruktory nemusí být triviální, ale pokud má standardní strukturu, může spolupracovat s jazykem C. Podobně třída s veřejnými a soukromými nestatickými datovými členy nebude mít standardní strukturu, ale může být triviální, a pak ji lze kopírovat pomocí memcpy.

Rozšíření jádra pro rychlejší kompilaci

editovat

Externí šablona

editovat

V C++03 musí překladač instanciovat šablonu vždy, když se v kompilační jednotce objeví plně specifikovaná šablona, např.:

template class std::vector<myclass>;

Pokud je šablona instanciována se stejnými typy v mnoha kompilačních jednotkách, může to výrazně prodloužit trvání překladu. V C++03 tomu nelze zabránit, ale C++11 zavádí externí deklarace šablon podobné externím deklaracím dat. Používají deklarátor extern:

extern template class std::vector<myclass>;

čímž říká překladači, aby v této kompilační jednotce instanciaci šablony neprováděl.

Rozšíření jádra pro lepší použití jazyka

editovat

Seznamy inicializátorů

editovat

C++11 rozšiřuje statickou inicializaci pole nebo struktury známou z C. Pomocí std::initializer_list<> umožňuje konstruktoru předat pole inicializačních parametrů uzavřených v {}.

// inicializace v C
int pole[] = {10, 10}; // inicializace pole

typedef struct {
    int a;
    int b;
} Struktura;

Struktura strukt = {10, 10}; // inicializace struktury

// inicializace v C++11
class Trida {
public:
    Trida(int par);
};

Trida pole[] = {10, 10}; // inicializace pole tříd

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

Trida pole = {1, 2}; // inicializace třídy

Typová inference

editovat

C++11 zavádí dvě nové metody, jak deklarovat proměnné. Klíčové slovo auto doplní typ proměnné automaticky podle přiřazované hodnoty. Funkce decltype(výraz) vrací typ parametru.

auto i = 15; // i bude int;
decltype(i) j; // j bude int;

Výhodu přináší u složitých typů, zkracuje zápis.

// C++03
for(std::vector<int>::const_iterator it = data.cbegin(); it != data.cend(); ++it);

// C++11
for(auto it = data.cbegin(); it != data.cend(); ++it);

For cyklus

editovat

Rozšiřuje o novou definici cyklu for. Tento cyklus je známý z jiných jazyků jako cyklus foreach.

int pole[10];

for(int& i : pole) {

}

Tento cyklus lze také použít pro kontejnery, které poskytují metody begin a end.

Lambda funkce

editovat

C++11 zavádí anonymní funkce (lambda funkce). Lambda funkce umožňuje deklarovat i definovat dočasnou funkci (například jako parametr funkce nebo uvnitř jiné funkce).

[externí_proměnné](parametry)->návratový_typ { tělo funkce; }

V hranatých závorkách lze předat proměnné z rozsahu platnosti, ve kterém je funkce definovaná. Lze předat všechny, nebo jen výčet a je možné je předat hodnotou nebo referencí.

[] // funkce nemá přístup k externím proměnným
[x, &y] // k proměnné x lze ve funkci přistupovat jako k hodnotě, k y jako k referenci
[&] // všechny proměnné jsou předány referencí
[=] // všechny proměnné jsou předány hodnotou
[&, x] // x je předáno hodnotou, všechny ostatní referencí

Příklad lambda funkce:

[](int x, int y) { return x + y; } // Návratový typ je implicitní, funkce nevidí žádné proměnné mimo rozsah své platnosti

int a = 10;
[&](int x, int y)->int { return x + y + a; } // Návratový typ je explicitní, funkce má přístup ke všem proměnným z rozsahu platnosti
                                             // ve které je definována, proměnné jsou předány referencí
[=](int x, int y)->int { return x + y + a; } // proměnné jsou předány hodnotou

Aliasy na jmenné prostory

editovat
namespace ab
{
    int zdvojnasob(int x)
    {
        return x*2;
    }
}
namespace x=ab;
x::zdvojnasob(5);//To samé jako ab::zdvojnasob(5)

Nová syntaxe typových aliasů

editovat
//C++98
typedef int my_type;
//C++11
typedef int my_type;//Zpětná kompatibilita
using my_type=int;//Nová syntaxe

//Toto pomocí typedef nelze:
template<typename T>using ptr=T*;

Alternativní syntaxe funkcí

editovat

Syntaxe deklarací funkcí ve standardním jazyce C byla pro tento jazyk naprosto dostatečná. Protože se jazyk C++ vyvinul z jazyka C, udržoval základní syntaxi a rozšířil ji, kde to bylo potřebné. Jak se jazyk C++ zesložiťoval, byl vystaven několika limitům, zvláště týkajících se deklarace šablon funkcí. Například v C++03 není toto možné:

template<classLhs, třída Rhs>
  Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret musí mít typ jako lhs+rhs

Typ Ret je cokoli, co vznikne sečtením typů Lhs a Rhs. Dokonce ani s výše uvedenou funkčností decltype je v C++11 následující zápis chybný:

template<classLhs, třída Rhs>
  decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} // V C++11 není dovoleno

Tato konstrukce není v C++ povolena, protože lhs a rhs zatím nebyly definovány; identifikátory nejsou platné, dokud analyzátor neprovede analýzu zbytku prototypu funkce.

Proto C++11 umožňuje zadat typ návratové hodnoty funkce až za seznamem parametrů a symbolem ->:[6]

template<classLhs, třída Rhs>
  auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

Tuto syntaxi lze používat i pro jednodušší 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;
}

Klíčové slovo auto je v tomto případě pouze syntaktickou záležitostí a neznamená automatické vyvozování typu. Od C++14 je možné návratový typ funkce za seznamem parametrů zcela vypustit a překladač si jej odvodí sám.[7]

Zlepšení konstrukce objektů

editovat

V C++03 není dovoleno konstruktorům třídy volat jiné konstruktory v seznamu inicializátorů této třídy. Každý konstruktor musí vytvořit veškeré členy své třídy sám nebo zavolat společnou členskou funkci, jako např.:

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 bázových tříd nemohou být přímo zpřístupněny odvozeným třídám; každá odvozená třída musí implementovat vlastní konstruktory, i když by stačil konstruktor základní třídy. Nekonstantní datové členy třídy nemohou být inicializovány v místě své deklarace, ale pouze v konstruktoru.

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

C++11 umožňuje, aby konstruktory volaly jiné konstruktory (což se nazývá delegace). To umožňuje, aby konstruktory využívaly chování jiných konstruktorů s minimem přidaného kódu. Delegace jsou dostupné v jiných jazycích, např. v Javě nebo v Objective-C.

V C++11 lze místo výše uvedeného kódu definujícího jeden konstruktor s parametrem a druhý bez parametru napsat:

class SomeType
{
    int number;

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

Stejného efektu by bylo možné dosáhnout tím, že by new_number bylo implicitním parametrem. Nová syntaxe však umožňuje, aby implicitní hodnota (42) byla vyjádřena v implementaci místo v rozhraní – což je výhodné pro údržbu knihoven, protože implicitní hodnoty funkčních parametrů jsou „zapečené“ v místech volání, zatímco delegování konstruktoru umožňuje změnit hodnotu bez nového překladu kódu používajícího knihovnu.

Delegace konstruktorů přináší určitý problém: zatímco v C++03 je na konci provádění libovolného konstruktoru objekt úplně vytvořen, v C++11 je objekt vytvořen, až když doběhnou všechny konstruktory. Protože je povoleno provádět více konstruktorů, každý delegující konstruktor bude pracovat na plně zkonstruovaném objektu pouze svého typu. Konstruktory odvozených tříd se budou provádět až po dokončení všech delegací v jejich základních třídách.

Pro konstruktory základních tříd umožňuje C++11 třídě určit, že konstruktory základní třída budou zděděny. Překladač C++11 tedy vygeneruje kód, který provede dědění a předání odvozené třídy základní třídě. Jde o přístup typu „všechno nebo nic“: buď budou předány všechny konstruktory dané základní třídy nebo žádný z nich. Také zděděný konstruktor bude zastíněn, pokud se shoduje se signaturou konstruktoru odvozené třídy, a existují omezení pro vícenásobnou dědičnost: konstruktory třídy nelze dědit ze dvou tříd, které používají konstruktory se stejnou signaturou.

Syntaxe je následující:

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

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

C++11 dovoluje tuto syntaxi pro inicializaci členů:

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

private:
    int value = 5;
};

Libovolný konstruktor této třídy bude inicializovat value na 5, pokud konstruktor sám nepředefinuje inicializaci. Proto výše uvedený prázdný konstruktor bude inicializovat value jak uvádí definice třídy, ale konstruktor s parametrem typu int bude inicializovat value na hodnotu tohoto parametru.

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

Explicitní předefinování a final

editovat

V C++03 může programátor omylem vytvořit novou virtuální funkci, když chtěl předefinovat funkci základní třídy. Například:

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

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

Předpokládáme, že <code Derived::some_func měla nahradit metodu základní třídy. Ale protože má jinou signaturu, vytvoří další virtuální funkci. To je častý problém, ke kterému dochází zvláště tehdy, když uživatel chce modifikovat základní třídu.

C++11 umožňuje tento problém řešit takto:

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

struct Derived : Base
{
    virtual void some_func(int) override; // chyba - nepředefinovává metodu základní třídy
};

Speciální identifikátor override znamená, že překladač bude kontrolovat základní třídu nebo třídy, zda existuje virtuální funkce přesně s touto signaturou. A pokud neexistuje, překladač oznámí chybu.

C++11 také umožňuje zabránit dědění z třídy nebo jednoduše zabránit předefinování metody v odvozených třídách. K tomuto účelu slouží speciální identifikátor final. Například:

struct Base1 final { };

struct Derived1 : Base1 { }; // chybné, protože třída Base1 byla označena jako final
struct Base2
{
    virtual void f() final;
};

struct Derived2 : Base2
{
    void f(); // chybné, protože virtuální funkce Base2::f byla označena jako final
};

V tomto příkladě příkaz virtual void f() final; deklaruje novou virtuální funkci, a zároveň brání tomu, aby ji odvozená třída předefinovala. Také způsobuje, že odvozené třídy nemohou používat funkce s tímto jménem a kombinací parametrů.

Ani override ani final nejsou klíčová slova. Technicky to jsou identifikátory atributů deklarátorů:

  • speciální význam jako atributy mají pouze v těchto specifických koncových kontextech (po všech specifikátorech typu, přístupu, deklaracích členů (typů struktura, třída nebo výčet) a specifikátorech deklarátorů, ale před inicializací nebo implementací kódu každého deklarátoru v seznamu deklarátorů oddělených čárkou);
  • nemění deklarovanou typovou signaturu a nedeklarují ani nepředefinovávají žádný nový identifikátor v jakémkoli rozsahu platnosti;
  • rozpoznané a přijaté atributy deklarátorů mohou být v budoucí verzi C++ rozšířeny (některá rozšíření pro konkrétní překladače již rozpoznávají další atributy deklarátorů pro volby generování kódu nebo optimalizační nápovědy pro překladač nebo pro generování dodatečných dat do přeloženého kódu, která jsou určena pro debuggery, linkery, a nasazení přeloženého kódu nebo pro poskytování přídavných atributů datové bezpečnosti specifických pro daný systém nebo pro zlepšení schopností reflexí v době běhu nebo pro poskytnutí dalších informací pro vázání pro interoperabilitu s jiný programovacími jazyky a běhovými systémy; tato rozšíření mohou využívat parametry zapsané v závorkách po identifikátoru atributu deklarátoru; aby tato rozšíření specifická pro určitý překladač vyhovovala normě ANSI, musí jejich jména začínat dvojitým podtržítkem).
  • na libovolném jiném místě mohou být tyto identifikátory použity pro nové deklarace (a pozdější použití, pokud jsou dostupné).

Konstanta a typ nulového ukazatele

editovat

V této části každý výskyt 0 je míněn jako „konstantní výraz, který je typu int, a vyhodnotí se na hodnotu 0“. Ve skutečnosti tento konstantní výraz může být libovolného celočíselného typu.

Od počátků jazyka C v roce 1972 měla konstanta 0 dvojí roli celého číslo a nulového ukazatele. Nejednoznačnost spjatá s dvojím významem hodnoty 0 se v jazyce C řešila makrem preprocesoru NULL, které se obvykle expanduje buď na ((void*)0) nebo na 0. C++ zakazuje implicitní konverze hodnot typu void * na jiné typy ukazatelů, čímž odstraňuje výhodu změny typu 0 na void *. V důsledku toho je pouze 0 povolena jako konstanta nulového ukazatele. To jde špatně dohromady s přetěžováním:

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

Pokud NULL je definované jako 0 (což v C++ obvykle je), příkaz foo(NULL); vyvolá foo(int), což je téměř určitě něco, co programátor nechtěl, a co naznačuje povrchní čtení kódu.

C++11 opravuje tento problém definováním nového klíčového slova nullptr, které slouží jako konstanta pro nulový ukazatel. Tato konstanta je typu nullptr_t, který lze implicitně zkonvertovat na libovolný typ ukazatele nebo ukazatele na člen, a porovnávat s libovolným ukazatelem. nullptr nelze implicitně zkonvertovat na celočíselný typ ani jej s celočíselnými typy porovnávat, kromě typu bool. Zatímco původní návrh uváděl, že rvalue typu nullptr_t nesmí být možné zkonvertovat na bool, pracovní skupina pro jádro jazyka rozhodla, že takové konverze jsou žádoucí pro konzistenci s normálními typy ukazatel. Navrhované změny znění byly v červnu 2008 jednomyslně odhlasovány do pracovního dokumentu.[1] Podobný návrh byl předložen také pracovní skupině pro normu jazyka C a byl přijat pro začlenění do normy C23.[8]

Kvůli zpětné kompatibilitě zůstává 0 platnou konstantou pro nulový ukazatel.

char *pc = nullptr; // OK
int *pi = nullptr; // OK
bool b = nullptr; // OK. b je nepravdivé.
int i = nullptr; // chyba

foo(nullptr); // volá foo(nullptr_t), ne foo(int);
/*
  Všimněte si, že foo(nullptr_t) skutečně volá foo(char *) ve výše uvedeném příkladě použitím implicitní konverze,
  pouze pokud žádné jiné funkce nepřetěžují kompatibilní typy ukazatele v rozsahu platnosti.
  Pokud existuje více přetížení, rezoluce selže kvůli nejednoznačnosti,
  pokud neexistuje explicitní deklarace foo(nullptr_t).

  V hlavičkových souborech pro standardní typy pro C++11 musí typ nullptr_t být deklarován jako:
      typedef decltype(nullptr) nullptr_t;
  ne jako:
      typedef int nullptr_t; // předchozí verze C++, ve kterých musí být NULL být definováno jako 0
      typedef void *nullptr_t; // ANSI C, které definuje NULL jako ((void*)0)
* /

Silně typované výčtové typy

editovat

V C++03 nejsou výčtové typy typově bezpečné. Efektivně to jsou celá čísla, i když jsou výčtové typy různé. To umožňuje porovnávat výčtové hodnoty různých výčtových typů. Jedinou bezpečností, kterou C++03 poskytuje, je, že celá čísla nebo hodnoty jednoho výčtového typu se implicitně nekonvertují na jiný výčtový typ. Podkladové celočíselné typy jsou implementačně závislé; proto je kód, které závisí na velikosti výčtových typů, nepřenositelný. Rozsah platnosti výčtových hodnot je obklopující rozsah platnosti. Proto dva různé výčtové typy ve stejném rozsahu platnosti nemohou mít stejná jména hodnot.

C++11 umožňuje speciální klasifikaci výčtových typů bez výše uvedených problémů. Používá deklaraci pomocí enum class (lze používat i enum struct):

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

Tento výčtový typ je typově bezpečný. Hodnoty enum class se implicitně nekonvertují na celá čísla. Proto je nelze porovnávat s celými čísly (výraz Enumeration::Val4 == 101 způsobí chybu při překladu).

Podkladový typ těchto výčtových tříd je vždy známý. Implicitní typ je int, ale může být předefinován na jiný celočíselný typ, jak je vidět v tomto příkladě:

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

Výčtové hodnoty starých výčtových typů mají rozsah platnosti rovný obklopujícímu rozsahu platnosti. Výčtové hodnoty nových výčtových typů mají rozsah platnosti daný výčtovou třídou. Ve výše uvedeném příkladě Val1 není definováno, ale Enum2::Val1 ano.

Existuje také přechodová syntaxe umožňující výčtovým hodnotám starého stylu poskytovat explicitní používání rozsahů platnosti a definice podkladového typu:

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

V tomto případě jsou výčtová jména definována s rozsahem platnosti výčtu (Enum3::Val1), ale pro zpětnou kompatibilitu jsou také umístěna v obklopujícím rozsahu platnosti.

V C++11 jsou také možné dopředné deklarace výčtových typů. Dříve výčtové typy nemohly být deklarovány dopředně, protože jejich velikost závisí na definici jejich členů. Pokud je velikost výčtového typu stanovena explicitně nebo implicitně, mohou být deklarovány dopředně:

enum Enum1; // Nepovolené ani v C++03 ani v C++11; podkladový typ nelze určit.
enum Enum2 : unsigned int; // Povolené v C++11, podkladový typ je zadán explicitně.
enum class Enum3; // Povolené v C++11, podkladový typ je int.
enum class Enum4 : unsigned int; // Povolené v C++11.
enum Enum2 : unsigned short; // V C++11 nepovolené, protože Enum2 byl dříve deklarován s odlišným podkladovým typem.

Pravá lomená závorka

editovat

Syntaktický analyzátor C++03 definuje >> vždy jako operátor posunu doprava nebo jako operátor načtení z proudu. S vnořenými deklaracemi šablon se však objevují sklony programátorů vynechávat mezery mezi dvojitými lomenými závorkami, což způsobí syntaktickou chybu.

C++11 vylepšuje specifikaci analyzátoru, takže tam, kde to má význam, bude několik pravých lomených závorek interpretováno jako uzavření seznamu argumentů šablony. Toto chování lze předefinovat pomocí závorek okolo výrazů v parametrech s použitím binárních operátorů >, >= nebo >>:

template<boolTest> třída SomeType;
std::vector<sometype<1>2>> x1; // Interpretováno jako std::vector položek typu SomeType<true>,
    // za čímž je „2 >> x1“, což není syntakticky správný deklarátor. 1 je pravdivé.
std::vector<sometype<(1>2)>> x1; // Interpretováno jako std::vector pro SomeType<false>,
    // následovaný deklarátorem „x1“, což je v C++11 syntakticky správně. (1>2) je nepravdivé.

Explicitní operátory konverze

editovat

Od C++98 lze používat klíčové slovo explicit jako modifikátor konstruktorů, aby se zabránilo použití konstruktorů s jedním argumentem jako implicitním operátorům konverze typu. Pro skutečné konverzní operátory to však nic neznamená. Například třída smart pointery může mít operator bool(), který umožní, aby se chovala více jako primitivní ukazatele: pokud obsahuje tuto konverzi, lze ji testovat pomocí s if (smart_ptr_variable) (což je pravdivé,, pokud ukazatel je nenulový a nepravdivé jinak). To však umožňuje i jiné, nežádoucí konverze. Protože v C++ je typ bool definován jako aritmetický typ, lze je implicitně konvertovat na celočíselné typy nebo dokonce typy s pohyblivou řádovou čárkou, což umožňuje matematické operace, které uživatel nezamýšlel.

V C++11 lze klíčové slovo explicit použít i na konverzní operátory. Stejně jako v případě konstruktorů zabrání použití těchto konverzních funkcí při implicitních konverzích. Nicméně jazykové kontexty, které specificky potřebují logickou hodnotu (podmínky if-příkazů a smyček, a operandy logických operátorů), se počítají jako explicitní konverze, a mohou tedy použít konverzní operátor bool.

Čisté řešení problému bezpečného bool je popsáno na anglických Wikibooks.

Šablonové aliasy

editovat

Typ definovaný pomocí typedef může být pouze přezdívkou (synonymem) pro jiný typ, včetně synonym pro specializaci šablon se zadanými všemi skutečnými argumenty šablony. Nové šablony pomocí typedef vytvářet nelze:

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

template <typename Second>
typedef SomeType<othertype, Second, 5> TypedefName; // Chyba!

Tuto konstrukci nelze přeložit. V C++11 je však možné použít šablonový alias:

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

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

Klíčové slovo using lze v C++11 také použít pro typový aliasing:

typedef void (*FunctionType)(double);  // Starý styl
using FunctionType = void (*)(double); // Nově zavedená syntaxe

Neomezené uniony

editovat

V C++03 existují omezení, jaké typy objektů mohou být členy unionů; union například nemůže obsahovat žádný objekt, který definuje netriviální konstruktor nebo destruktor. C++11 některá z těchto omezení odstraňuje.[2]

Pokud má člen unionu netriviální speciální členskou funkci, překladač nebude generovat ekvivalentní členskou funkci pro union, ale taková funkce musí být definována ručně.

Jednoduchý příklad použití unionu v C++11:

#include <new> // Pro operátor 'new'.

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

union U
{
    int z;
    double w;
    Bod b; // Chyba C++03; povoleno v C++11.
    U() {} // Kvůli členu Bod je nutné konstruktor explicitně definovat.
    U(const Bod& pt) : b(pt) {} // Vytvoří objekt Bod pomocí seznamu inicializátorů.
    U& operator=(const Bod& pt) { new(&p) Bod(pt); return *this; } // Přiřadí objekt Bod použitím 'new'.
};

Změny neovlivní existující kód, protože pouze uvolňují původní pravidla.

Vylepšení funkčnosti jádra jazyka

editovat

Tyto vlastnosti umožňují dělat věci, které byly dříve nemožné, jejich zápis byl příliš složitý nebo vyžadovaly nepřenositelné knihovny.

Variadické šablony

editovat
Podrobnější informace naleznete v článku Variadická šablona.

V C++11 mohou mít šablony proměnný počet parametrů, což mj. umožňuje definovat typově bezpečné variadické funkce.

Nové řetězcové literály

editovat

Možnosti řetězcových literálů v C++03 byly značně omezené; kromě řetězců dostupných v jazyce C zapisovaných v uvozovkách, které vytvářejí pole typu const char zakončená znakem s kódem nula, poskytuje pouze řetězce vytvářející pole typu const wchar_t také zakončené znakem s kódem nula; tyto řetězce širokých znaků se zapisují pomocí L"". wchar_t je široký znak nedefinované velikosti a sémantiky. Žádný z těchto literálů nenabízí podporu řetězcových literálů v UTF-8, UTF-16 nebo jiném kódování Unicode.

C++11 podporuje tři kódování Unicode: UTF-8, UTF-16 a UTF-32. Do definice typu char byla doplněno, že jeho velikost je minimálně taková, aby umožňovala ukládání osmibitového kódování UTF-8, a dostatečně velká, aby obsahovala všechny znaky základní znakové sady překladače. Původní norma C++ obsahovalo pouze druhou podmínku, u první se spoléhalo na normu jazyka C, která zaručuje velikost alespoň 8 bitů. C++11 navíc přidává dva nové znakové typy: char16_t pro UTF-16 a char32_t pro UTF-32.

Řetězcové literály pro uvedená kódování se píší takto:

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

Typ prvního řetězec je obvyklý const char[]. Typ druhého řetězce je const char16_t[] (předpona je malé 'u'). Typ třetího řetězce je const char32_t[] (předpona je velké 'U').

Do řetězcových literálů v Unicode lze v C++11 snadno vkládat kódové body Unicode:

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

Číslo za \u je v šestnáctkově soustavě, a na rozdíl od celočíselných konstant neobsahuje předponu 0x. Identifikátor \u reprezentuje 16bitový kódový bod Unicode; pro vložení 32bitového kódového bodu slouží \U následované 32bitovým šestnáctkovým číslem. Těmito zápisy lze zadávat pouze platné kódové body Unicode; jsou zakázané např. kódové body z rozsahu U+D800–U+DFFF, které jsou rezervované pro náhradní páry v kódování UTF-16.


C++11 zavádí tzv. syrové řetězcové literály, ve kterých se před znaky uvozovky " a obrácené lomítko \ nepíší obrácena lomítka, čímž lze zpřehlednit zápis literálů pro soubory XML, skriptovací jazyky nebo regulární výrazy. Existují 2 formy zápisu, v obou literál začíná znakem R:

R"(Řetězcová obsahující \ a " )"
R"sep(Řetězcová obsahující \ a " )sep"

V prvním případě patří do řetězce vše mezi "( a )". Znaky " a \ nejsou escapovány. Ve druhém případě začíná řetězec "sep(, a končí )sep". sep je posloupnost nejvýše 16 znaků, kterými nesmí být mezery, řídicí znaky, a znaky (, ) nebo \. Zápis R"oddel("(a-z)")oddel" je ekvivalentní s "\"(a-z)\"".

Syrové řetězcové literály lze používat i pro řetězce Unicode nebo širokých znaků:

u8R"XXX(Toto je "syrový UTF-8" řetězec.)XXX"
uR"*(Toto je "syrový UTF-16" řetězec.)*"
UR"(Toto je "syrový UTF-32" řetězec.)"

Uživatelem definované literály

editovat

C++03 poskytuje různé literály. Zápis 12.5 je literál, který je vyhodnocen překladačem jako typ double s hodnotou 12,5. Přidáním sufixu f v 12.5f dává hodnotu 12,5 typu float. Sufixové modifikátory literálů jsou opraveny podle C++ specifikace, a kód v C++03 nemůže vytvářet nové modifikátory literálů.

C++11 naproti tomu umožňuje uživateli definovat nové druhy modifikátorů literálů, které budou vytvářet objekty vycházející z řetězce znaků, které literál mění.

Transformace literálů je předefinována ve dvou různých fázích: syrové a vařené. Syrový literál je posloupnost znaků určitého typu, zatímco vařený literál má zvláštní typ. Literál 1234 v C++ je, protože jde o syrový literál, posloupností znaků '1', '2', '3', '4'. Jako vařený literál, je to celé číslo 1234. C++ literál 0xA je v syrovém tvaru '0', 'x', 'A', zatímco ve vařeném tvaru je to celé číslo 10.

Literály mohou být rozšířeny v syrové i vařené formě, s výjimkou řetězcových literálů, které lze zpracovávat pouze ve vařené formě. Toto výjimka je způsobena faktem, že řetězce mají předpony, které ovlivňují určitý význam a typ příslušných znaků.

Všechny uživatelem definované literály se rozlišují příponami; definování předpon literálů není možné. Všechny přípony začínající libovolným znakem kromě podtržítka (_) jsou podle normy rezervované. Proto všechny uživatelem definované literály musí mít přípony začínající podtržítkem (_).[9]

Jak mají být uživatelem definované literály zpracovány, se definuje operátorem literálu, zapisovaným operator "". Příklad:

OutputType operator "" _mysuffix(const char * literal_string)
{
    // předpokládá, že OutputType má konstruktor, který bere parametr const char *
    OutputType ret(literal_string);
    return ret;
}

OutputType some_variable = 1234_mysuffix;
// předpokládá, že OutputType má metodu get_value(), která vrací double
assert(some_variable.get_value() == 1234.0)

Přiřazovací příkaz OutputType some_variable = 1234_mysuffix; provede kód definovaný uživatelem definovanou literálovou funkcí. Této funkci se předává parametr "1234" ve formě řetězce ve stylu jazyka C, tj. zakončeného znakem s kódem nula.

Druhý možný mechanismus pro zpracování celočíselných syrových literálů a syrových literálů s pohyblivou řádovou čárkou používá variadické šablony:

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

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

Toto instanciuje funkce na zpracování literálů, jako operator "" _tuffix<'1', '2', '3', '4'>(). V tomto tvaru není řetězec ukončen znakem s kódem nula. Hlavním účelem je umožnění použití klíčového slova constexpr v C++11 pro zajištění, že překladač bude transformovat literál v době překladu, předpokládá OutputType lze zkonstruovat pomocí constexpr a jde o kopírovatelný typ, a funkce na zpracování literálu je constexpr funkce.

Pro numerické literály je typ vařeného literálu buď unsigned long long pro celočíselné literály nebo long double pro literály s pohyblivou řádovou čárkou. (Poznámka: Nejsou třeba žádné celočíselné typy se znaménkem, protože literály se znaménkem jsou analyzovány jako výrazy, v nichž je znaménko prefixovým operátorem následovaným číslem bez znaménka.) Neexistuje žádná alternativní forma šablony:

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

OutputType some_variable = 1234_suffix; // Používá přetížení 'unsigned long long'.
OutputType another_variable = 3.1416_suffix; // Používá přetížení 'long double'.

V souladu s dříve zmíněnými novými předponami řetězců pro řetězcové literály lze použít:

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; // Používá přetížení 'const char *'.
OutputType some_variable = u8"1234"_ssuffix; // Používá přetížení 'const char *'.
OutputType some_variable =  L"1234"_ssuffix; // Používá přetížení 'const wchar_t *'.
OutputType some_variable =  u"1234"_ssuffix; // Používá přetížení 'const char16_t *'.
OutputType some_variable =  U"1234"_ssuffix; // Používá přetížení 'const char32_t *'.

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

Paměťový model pro multithreading

editovat
Související informace naleznete také v článku Memory model (computing).

C++11 standardizuje podporu vláken.

Tato podpora má dvě složky: paměťový model, který umožňuje koexistenci několika vláken v programu a knihovna podpora pro interakce mezi vlákna. (Viz sekce Nástroje pro threading.)

Paměťový model definuje, když několik vlákna mohou přístup stejný paměťový umístění, a udává, když aktualizuje jedním vlákno se stane viditelný na jiný vlákna.

Vláknové lokální úložiště

editovat

Ve vícevláknovém prostředí je běžné, že každé vlákno má některé jedinečné proměnné. To se děje pro lokální proměnné funkce, ale neděje se to pro globální a statické proměnné.

Vláknové lokální úložiště s novým trváním (vedle existujících trvání static, dynamic a automatic) je indikováno specifikátorem úložiště thread_local.

Libovolný objekt, který by měl mít statickou dobu trvání (tj. životnost po celou dobu provádění programu), může mít místo toho dobu trvání thread_local. Záměrem je, aby stejně jako jakákoli jiná proměnná se statickým trváním mohl být objekt lokální ve vlákně inicializován pomocí konstruktoru a zrušen pomocí destruktoru.

Explicitně implicitní speciální členské funkce

editovat

V C++03 překladač pro třídy, které je samy neposkytují, poskytuje implicitní konstruktor, kopírovací konstruktor, kopírovací operátor přiřazení (operator=) a destruktor. Tyto implicitní funkce může programátor nahradit definováním vlastních verzí. C++ také definuje několik globálních operátorů (např. operator new), které fungují pro všechny třídy, a které programátor může předefinovat.

Nad vytvářením těchto implicitních funkcí však je velmi malá kontrola. Provedení třída ze své podstaty necopyable, například může být uděláno deklarováním privátního kopírovacího konstruktoru a kopírovacího operátoru přiřazení bez jejich definování. Snaha používat tyto funkce je narušením pravidla jedné definice (ODR). Sice není vyžadována diagnostická zpráva normou[10] porušení těchto pravidel mohou způsobit chybu při linkování kódu.

V případě implicitního konstruktoru nebude překladač generovat implicitní konstruktor pokud je třída definována s libovolnými konstruktory. To je užitečné v mnoha případech, ale je také užitečné umožnit, aby třídy měly jak specializované konstruktory tak překladačem generovaný implicitní konstruktor.

C++11 umožňuje specifikovat, že se pro třídu vytvořit implicitní speciální členská funkce, stejně jako implicitní speciální členskou funkci smazat.[11] Následující třída explicitně vyžaduje, aby byl vytvořen implicitní konstruktor:

class SomeType
{
    SomeType() = default; // Implicitní konstruktor je explicitně uveden.
    SomeType(OtherType hodnota);
};

Explicitně smazané funkce

editovat

C++11 umožňuje explicitně zakázat (smazat) funkci. Toho lze použít pro zabránění implicitním typovým konverzím. Specifikátor = delete lze použít pro znemožnění volání funkce s určitými typy parametrů.[11] Například:

void noInt(double i);
void noInt(int) = delete;

Pokus zavolat funkci noInt() s parametrem typu int překladač ohlásí jako chybu, místo toho aby provedl tichou konverzi na typ double. Volání funkce noInt() s parametrem typu float stále funguje.

Pomocí šablony je také možné zakázat volání funkce s libovolným jiným typem parametru než double:

double onlyDouble(double d) {return d;}
template<typename T> double onlyDouble(T) = delete;

Volání onlyDouble(1.0) bude fungovat, ale pro onlyDouble(1.0f) překladač ohlásí chybu.

Členské funkce tříd a konstruktory mohou být také smazány. Je například možné zabránit kopírování objektů třídy smazáním kopírovacího konstruktoru a přiřazovacího operátoru operator =:

class NonCopyable
{
    NonCopyable();
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

Typ long long int

editovat

V C++03 je největším celočíselným typem long int. Co se týče jeho velikosti, je pouze zaručeno, že má alespoň tolik použitelných bitů jako int. V některých implementacích má long int velikost 64 bitů a v jiných 32 bitů. C++11 proto přidává nový celočíselný typ long long int, u něhož je zaručeno, že je alespoň stejně velký jako long int, a že má alespoň 64 bitů. Tento typ byl původně zaveden normou C99 jazyka C, a většina překladačů C++ jej již podporovala jako rozšíření.[12][13]

Statické aserce

editovat

C++03 poskytuje dvě metody testování asercí: makro assert a direktivu preprocesoru #error. Žádná z nich však není vhodná pro použití v šablonách: makro testuje aserci v době provádění, a direktiva preprocesoru testuje aserci při zpracování preprocesorem, což je před instanciací šablony. Žádná není vhodná pro testování vlastností, které jsou závislé na parametrech šablony.

Nový nástroj přináší nový způsob testování aserce v době překladu s použitím nového klíčového slova static_assert. Deklarace předpokládá tento tvar:

static_assert (konstantní-výraz, chybová-zpráva);

Následující příklady ukazují použití static_assert:

static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI je nepřesný!");
template<classT>
struct Check
{
    static_assert(sizeof(int) <= sizeof(T), "T není dostatečně velký!");
};
template<classIntegrál>
Integral foo(Integral x, Integral y)
{
    static_assert(std::is_integral<integral>::value, "parametr foo() musí být celočíselného typu.");
}

Když je konstantní výraz nepravdivý (false), překladač vyprodukuje chybovou zprávu. První příklad je podobný direktivě preprocesoru #error, i když preprocessor podporuje pouze celočíselné typy.[14] Naproti tomu, v druhém příkladu je aserce zkontrolována v každé instanciaci šablonové třídy Check.

Statické aserce jsou užitečné také mimo šablony. Například určitá implementace algoritmu může záviset na tom, že velikost long long je větší než int, což standard nezaručuje. Takový předpoklad je platný na většině systémů a překladačů, ale ne na všech.

Umožnění aplikace sizeof na členy třídy bez explicitního objektu

editovat

V C++03 je možné používat operátor sizeof na typy a objekty. Toto však nelze provést:

struct SomeType { OtherType clen; };

sizeof(SomeType::clen); // Dostupné v C++11, nedostupné v C++03.

Toto musí vracet velikost typu OtherType. V C++03 je to zakázané a způsobí to chybu při překladu. V C++11 je to dovolené. Totéž je možné pro operátor alignof zavedený v C++11.

Ovlivňování zarovnání objektu a dotaz na něj

editovat

C++11 umožňuje proměnná zarovnání být dotazovaný a řízený s alignof a alignas.

Operátor alignof bere typ a vrátí hranice mocninu 2 byte na který typ instancemi musí být přidělené (protože std::size_t). Když daný reference typ alignof vrátí zarovnání referencovaného typu; pro pole vrací zarovnání typu prvku.

Specifikátor alignas řídí zarovnání paměti pro proměnnou. Parametrem specifikátoru je konstanta nebo typ; je-li použit typ, je alignas(T) zkratkou za alignas(alignof(T)). Například pro zajištění, že pole typu char musí být správně zarovnané pro uložení hodnoty typu float:

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

Umožnění implementace garbage collectoru

editovat

Předchozí normy C++ umožňovaly implementaci vlastního garbage collectoru pomocí set_new_handler, ale pro jeho použití nedefinovaly dosažitelnost objektu. C++11 definuje podmínku, za které jsou hodnoty ukazatele „bezpečně odvozeny“ z jiné hodnoty. Implementace může specifikovat, že vyžaduje striktní bezpečnost ukazatelů, aby ukazatele, které nejsou odvozeny podle těchto pravidel, se mohly stát neplatnými.

Atributy

editovat

C++11 poskytuje standardizovanou syntaxi pro rozšiřování jazyka. Taková rozšíření se tradičně zadávala pomocí direktivy #pragma nebo pomocí proprietárních klíčových slov (jako __attribute__ pro GNU překladač nebo __declspec pro překladač od Microsoftu). S novou syntaxí je možné přidané informace specifikovat ve formě atributu zapsaného ve dvojitých hranatých závorkách. Atribut lze aplikovat na různé části 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 se atribut attr1 uplatní na typ proměnné i, attr2 a attr3 na samotnou proměnnou, attr4 platí pro příkaz if a vendor::attr5 se uplatní na příkaz return. Obecně (s několika výjimkami) atribut specifikovaný pro pojmenovanou entitu je umístěn po jméně, a před entitou, jinak, jak je ukázáno výše, může být několik atributů uvedeno v jedné dvojici dvojitých hranatých závorek, přidané argumenty mohou být poskytnuty pro atribut, a atributy mohou mít rozsah platnosti podle jmenných prostorů proprietárních atributů.

Doporučuje se, aby atributy neměly žádný sémantický význam v jazyce, a aby se neměnil smysl programu, pokud jsou ignorovány. Atributy mohou být užitečné pro poskytnutí informace, která například pomůže překladači vydat lepší diagnostiku nebo optimalizovat generovaný kód.

C++11 poskytuje dva standardní atributy: noreturn, který udává, že funkce nemá návratovou hodnotu, a carries_dependency, který pomáhá optimalizovat multithreadový kód uvedením, že argumenty funkce nebo její návratová hodnota přenášejí závislost.[ujasnit]

Změny standardní knihovny C++

editovat

Ve standardní knihovně C++11 bylo zavedeno několik nových vlastností. Mnoho jich mohlo být implementováno i podle starší normy, ale některé spoléhají (ve větší či menší míře) na nové základní vlastnosti C++11.

Velká část nové knihovny byla definována v dokumentu C++ Standards Committee's Library Technical Report (nazývaný TR1), který byl publikován v roce 2005. Různé plné a částečné implementace TR1 jsou aktuálně dostupné použitím jmenného prostoru std::tr1. Pro C++11 byly přesunuty do jmenného prostoru std. Protože však TR1 vlastnosti byly přeneseny do standardní knihovny C++11, byly nahrazeny příslušnými jazykovými vlastnostmi C++11, které v počáteční TR1 verzi nebyly dostupné. Také mohly být nahrazeny vlastnostmi, které byly dostupné v C++03, ale nebyly částí původní specifikace v TR1.

Rozšíření standardní knihovny

editovat

Nové hlavičkové soubory:

Vylepšení komponent standardní knihovny

editovat

C++11 poskytuje několik nových vlastností, které mohou s výhodou využívat již existující komponenty standardní knihovny. Většina kontejnerů standardní knihovny může například využívat podporu přesunových konstruktorů používajících Rvalue reference, jak pro rychlé přesuny těžkých kontejnerů tak pro přesun obsahu těchto kontejnerů na jiné paměťové lokace. Tam, kde to bylo možné, byly komponenty standardní knihovny vylepšeny o nové jazykové vlastnosti definované v C++11, mimo jiné pro:

  • rvalue reference a s nimi související podporu přesunu
  • podporu kódovacích jednotek UTF-16 a kódovacích jednotek UTF-32 pro znakové typy Unicode
  • variadické šablony (spolu s Rvalue referencemi pro umožnění dokonalého forwardingu)
  • výrazy konstantní v době překladu
  • decltype
  • explicit konverzní operátory
  • funkce deklarované defaulted nebo smazaný

Od předešlé normy C++ uplynulo hodně času, během něhož bylo vytvořeno množství kódu, který používá standardní knihovnu, při čemž byly odhaleny složky standardní knihovny, které bylo možné vylepšit. Příkladem je komponenta allocator. Do jazyka C++11 byl začleněn nový model alokátorů založený na oblastech platnosti, který doplňuje předchozí model.

Nástroje pro threading

editovat

C++03 poskytuje paměťový model, který podporuje threading, ale primární podpora pro skutečné použití threadingu přišla až se standardní knihovnou C++11.

Knihovna definuje třídu (std::thread) s podporou vláken, která používá objekt funkce (s volitelnou řadou argumentů, které ji lze předat) pro jeho spuštění v novém vlákně. Vlákno je možné pozastavit, dokud neskončí jiné běžící vlákno s využitím členské funkce std::thread::join() pro spojování vláken. Tam, kde je to možné, je poskytnut přístup k podkladovému nativnímu objektu nebo objektům vlákna pro operace podporované použitou výpočetní platformou pomocí členské funkce std::thread::native_handle().

Pro synchronizaci vláken byly do knihovny přidány příslušné mutexy (std::mutex, std::recursive_mutex, atd.) a podmínkové proměnné (std::condition_variable a std::condition_variable_any), které jsou dostupné prostřednictvím zámků RAII (Resource Acquisition Is Initialization) (std::lock_guard a std::unique_lock) a zamykacích algoritmů pro snadné použití.

Pro vysokou výkonnost při nízkoúrovňové práci je někdy potřebná komunikace mezi vlákna bez režie mutexů. K tomu se používají atomické operace nad paměťovými místy. Ty mohou volitelně specifikovat minimální omezení viditelnosti paměti potřebná pro danou operaci. K tomuto účelu lze také použít explicitní paměťové bariéry.

Vláknová knihovna C++11 obsahuje také futures a promises pro předávání asynchronních výsledků mezi vlákny a std::packaged_task pro zabalení volání funkcí, která mohou generovat takový asynchronní výsledek. Návrh futures byl kritizován, protože postrádá způsob, jak kombinovat futures a kontrolovat dokončení jednoho slibu uvnitř sady slibů.[15]

Další vysokoúrovňové nástroje pro práci s vlákny, např. fondy vláken byly odloženy do budoucí technické zprávy o C++. Nejsou součástí C++11, ale očekává se, že jejich výsledná implementace bude postavena zcela nad vlastnostmi vláknové knihovny.

Nový prostředek std::async poskytuje pohodlnou metoda spouštění úloh a jejich navázání na std::future. Uživatel si může vybrat, zda má být úloha spuštěna asynchronně na samostatném vlákně nebo synchronně na vlákně, které čeká na hodnotu. Implicitně si implementace může vybrat, což poskytuje snadný způsob, jak využít výhodu hardwarové souběžnosti bez oversubscription, a poskytuje některé výhody fondu vláken pro jednoduchá použití.

Uspořádaná n-tice (anglicky tuple) je kolekce předem daných rozměrů složená z heterogenních objektů. N-tice lze považovat za zobecnění členských proměnných struktur.

C++11 verze TR1 typu n-tic využívají C++11 vlastností jako variadické šablony. Pro rozumnou implementaci TR1 verze vyžadovaly implementací definovaný maximální počet obsažených typů, a značné použití triků s makry. Oproti tomu, implementace verze C++11 nevyžaduje žádný explicitní implementací definovaný maximální počet typů. I když překladače budou mít interní maximální hloubku rekurze pro instanciaci šablon (což je normální), n-tice podle C++11 nebudou tuto hodnotu ukazovat uživateli.

S použitím variadických šablon vypadá deklarace třídy n-tic takto:

template <class...Types> class tuple;

Příklad definice a použití typu n-tice:

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);    // Přiřadí do 'lengthy' hodnotu 18.
std::get<3>(proof) =  Krásný!; // Změní čtvrtý prvek n-tice.

Je možné vytvořit n-tici proof bez definování jejího obsahu, ale pouze pokud typy prvků n-tice mají implicitní konstruktory. Navíc je možné přiřadit n-tici do jiné n-tice; pokud dva typy n-tic jsou stejné, každý typ prvku musí mít kopírovací konstruktor; jinak, každý typ prvku n-tice na pravé straně operátoru přiřazení musí být zkonvertovatelný na typ odpovídajícího prvku na levé straně n-tice nebo odpovídající typ prvku v n-tici na levé straně musí mít 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, první dva prvky mohou být zkonvertovány,
         // třetí prvek lze vytvořit z 'const char *'.

Stejně jako std::make_pair pro std::pair existuje std::make_tuple pro automatické vytváření std::tuple s použití vyvozování typů; pro deklarování takových n-tic lze použít auto. std::tie vytváří n-tice lvalue reference pro usnadnění rozbalování n-tic. Přitom také může pomoci std::ignore. Příklad:

auto zaznam = std::make_tuple(Hari Ram, New Delhi, 3.5, 'A');
std::string name ; float gpa ; char grade ;
std::tie(jmeno, std::ignore, gpa, grade) = zaznam ; // std::ignore umožňuje vypustit jméno města
std::cout << jmeno << ' ' << gpa << ' ' << grade << std::endl ;

Jsou dostupné relační operátory (mezi n-ticemi se stejným počtem prvků), a jsou dostupné dva výrazy pro zjištění charakteristik n-tice (pouze během překladu):

  • std::tuple_size<t>::value vrátí počet prvků v n-tice T,
  • std::tuple_element<i, T>:: typ vrátí typ objektu s číslem I v n-tici T.

Hašovací tabulky

editovat

Zahrnutí hašovacích tabulek (neuspořádaných asociativních kontejnerů) do standardní knihovny C++ bylo jedním z nejčastěji se opakujících požadavků, který nebyl obsažen v C++03 pouze z časových důvodů. I když jsou v nejhorším případě (při existenci mnoha kolizí) hašovací tabulky méně efektivní než vyvážené stromy, v mnoha reálných aplikacích fungují lépe.

Kolize jsou řešené pouze pomocí lineárního řetězení, protože komise nepovažovala za vhodné standardizovat řešení otevřeného adresování, které přináší množství vnitřních problémů (především když je povoleno mazání položek). Aby se zabránilo kolizím jmen s nestandardními knihovnami, které vyvinuly vlastní implementace hašovacích tabulek, byla místo “hash” použita předpona “unordered”.

Nová knihovna má čtyři typy hašovacích tabulek, které se liší podle toho, zda dovolují prvky se stejným klíčem (jedinečné klíče nebo ekvivalentní klíče), a 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ích vyhledávacích stromech s předponou unordered_.

Typ hašovací tabulky 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 kladené na kontejnervou třídu, a mají všechny metody potřebné pro přístup k prvkům: insert, erase, begin, end.

Hašovací tabulky nevyžadovaly žádné rozšíření jádra jazyka C++ (i když implementace budou využívat různé vlastnosti jazyka C++11), pouze malé rozšíření hlavičkového souboru <functional> a zavedení hlavičkových souborů <unordered_set> a <unordered_map>. Žádné další změny v existujících standardních třídách nebyly potřebné, a nové rysy nezávisejí na jiných rozšířeních standardní knihovny.

std::array a std::forward_list

editovat

Kromě hašovací tabulek byly do standardní knihovny přidány dva další kontejnery. std::array je kontejner pevné velikosti, který je efektivnější než std::vector, ale bezpečnější a snáze použitelný než pole ve stylu jazyka C. std::forward_list je jednoduchý spojový seznam, který poskytuje prostorově efektivnější ukládání než obousměrný spojový seznam std::list, když není potřeba obousměrné procházení.

Regulární výrazy

editovat

Nová knihovna definovaná v novém hlavičkovém souboru <regex>, je tvořena několika novými třídami:

  • regulární výrazy jsou reprezentovány instancí šablonové třídy std::regex;
  • výskyty jsou reprezentovány instancí šablonové třídy std::match_results,
  • pro iteraci přes všechny možnosti, jak napasovat regulární výraz na řetězec, se používá std::regex_iterator

Funkce std::regex_search se používá pro samotné vyhledávání, zatímco pro ‘vyhledání a nahrazení’ se používá funkce std::regex_replace, která vrací upravený řetězec.[16]

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

#include <regex>
const char *vzorek = R([^ ,.\t\n]+); // najde slova oddělená mezerou, čárkou, tečkou, tabelátorem, znakem konce řádku

std::regex rgx(vzorek); // pro neplatný vzorek vyhodí výjimku

const char *target = Unseen University - Ankh-Morpork;

// Použij regex_iterator pro identifikaci všech slov s 'target' odděleným znaky 'vzorku'.
auto iter =
    std::cregex_iterator(target, target + strlen(target), rgx);

// ukončí iterátor posloupnosti
auto end =
    std::cregex_iterator();

for (; iter != end; ++iter)
{
  std::string match_str = iter->str();
  std::cout << match_str << '\n';
}

Knihovna <regex> nevyžaduje žádné úpravy libovolné existující hlavičkových souborů (i když bude používat jim kde je to vhodné) ani rozšíření jádra jazyka. V POSIX iverzi jazyka C jsou regulární výrazy také dostupné pomocí C POSIX knihovna#regex.h.

Univerzální smart pointery

editovat
Podrobnější informace naleznete v článku Smart_pointer#C++ smart pointers.

C++11 poskytuje std::unique_ptr, a vylepšení na std::shared_ptr a std::weak_ptr z TR1. std::auto_ptr je nedoporučovaný.

Rozšiřitelné nástroje pro generování náhodných čísel

editovat

Standardní knihovna jazyka C umožňuje generovat pseudonáhodná čísla pomocí funkce rand. Algoritmus je plně delegován na knihovnu dodavatele. C++ zdědil toto funkčnost beze změn, ale C++11 poskytuje novou metodu pro generování pseudonáhodných čísel.

Vytváření náhodných čísel v C++11 je rozděleno na dvě části: generátor, který obsahuje generátor stavu náhodných čísel a produkuje pseudonáhodná čísla; a rozdělení, které určuje rozsah a matematické rozdělení výsledku. Tyto dvě části jsou zkombinovány pro vytvoření objektu generátoru náhodných čísel.

Kromě standardní funkce rand z jazyka C, C++11 mechanismus přichází se třemi základními strojovými algoritmy generátoru:

C++11 také poskytuje několik nejpoužívanějších rozdělení:

Generátor a distribuci lze kombinovat jako v tomto příkladě:

#include <random>
#include <functional>

std::uniform_int_distribution<int> rozdeleni(0, 99);
std::mt19937 stroj; // Mersenneho twister MT19937
auto generator = std::bind(rozdeleni, stroj);
int random = generator(); // Generuje rovnoměrně rozdělená celá čísla mezi 0 a 99.
int random2 = distribution(stroj); // Generují jiný vzorek přímo pomocí objektů rozdělení a stroj.

Adaptér

editovat

Adaptér (anglicky wrapper) se získá z instance šablony třídy reference_wrapper. Adaptéry jsou podobné normálním referencím (&) v jazyce C++. Pro získání adaptéru z libovolného objektu se používá funkční šablona ref (pro konstantní reference cref).

Adaptéry jsou užitečné především pro funkční šablony, kde jsou potřeba reference na parametry, nikoli kopie:

// Tato funkce dostane odkaz na parametr 'r' a inkrementuje jej.
void func (int &r) { r++; }

// Funkční šablona.
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>' je instanciováno
                 // pak 'i' nebude změněno.
    std::cout << i << std::endl; // Vypíše -> 0

    g (func, std::ref(i)); // 'g<void(int &r),reference_wrapper<int>>' je instanciováno
                           // pak 'i' bude změněno.
    std::cout << i << std::endl; // Vypíše -> 1
}

Tento nový nástroj byl přidán k existujícímu hlavičkovému souboru <functional> a nevyžaduje další rozšíření jazyka C++.

Polymorfní adaptéry pro objekty funkcí

editovat

Polymorfní adaptéry pro objekty funkcí se sémanticky a syntakticky podobají ukazatelům na funkce, ale jsou méně striktně vázaný a mohou se bez rozdílů odkazovat na cokoli, co lze volat (ukazatele na funkce, ukazatele na členské funkce nebo funktory), jejichž argumenty jsou kompatibilní s argumenty adaptéru.

Příklad může vyjasnit jeho charakteristiky:

std::function<int(int, int)> func; // Vytvoření adaptéru použitím
                                 // šablonové třídy 'function'.
std::plus<int> add; // 'plus' je deklarováno jako 'šablona<classT> T plus( T, T ) ;'
                    // pak 'add' je typu 'int add( int x, int y )'.
func = add; // OK - Parametry a návratové typy jsou stejné.

int = func (1, 2); // POZNÁMKA: pokud se adaptér 'func' neodkazuje na žádnou funkce,
                   // bude vyhozena výjimka 'std::bad_function_call'.

std::function<bool(short, short)> func2 ;
if (!func2)
{
    // Pravdivé, protože do 'func2' ještě nebyla přiřazena funkce.

    bool adjacent(long x, long y);
    func2 = &adjacent; // OK - Parametry a návratové typy jsou konvertovatelné.

    struct Test
    {
        bool operator()(short x, short y);
    };
    Test vozidlo;
    func = std::ref(vozidlo); // 'std::ref' je šablona funkce, které vrací adaptér
                              // členské funkce 'operator()' struktury 'vozidlo'.
}
func = func2; // OK - Parametry a návratové typy jsou konvertovatelné.

Šablonová třída function je definována v hlavičkovém souboru <functional> bez potřeby měnit jazyk C++.

Typ rysy pro metaprogramování

editovat

Metaprogramování je vytváření programů, které vytvářejí nebo mění jiný program (nebo sebe sama). K tomu může docházet při překladu nebo při provádění. Standardizační komise C++ se rozhodla zavést knihovnu pro metaprogramování během překladu se šablonami.

Následující metaprogram používá rekurzi instancí šablon pro výpočet celočíselných mocnin podle normy C++03:

template<intB, int N>
struct Pow
{
    // rekurzivní volání a rekombinace.
    enum{ hodnota = B*Pow<b, N-1>::value };
};

template< int B >
struct Pow<b, 0>
{
    // ''N == 0'' podmínka pro ukončení.
    enum{ hodnota = 1 };
};
int tri_na_ctvrtou = Pow<3, 4>::value;

Mnoho algoritmů může pracovat s různými typy dat; šablony v C++ podporují generické programování a činí kód kompaktnější a užitečnější. Často však algoritmy potřebují informace o typech dat, které používají. Tyto informace lze získat při instanciaci šablony třídy pomocí typových rysů.

Typové rysy mohou identifikovat kategorii objektu a všechny charakteristiky třídy (nebo struktury). Jsou definovány v novém hlavičkovém souboru <type_traits>.

V dalším příkladě existuje šablonová funkce ‘elaborate’, který v závislosti na daný datové typy, bude instanciovat jeden ze dvou navržený algoritmy (Algoritmus::do_it).

// Poprvé způsob of fungující.
template< bool B > struct Algoritmus
{
    template<classT1, class T2> static int do_it (T1 &, T2 &) { /*...*/ }
};

// Druhý způsob of fungující.
template<> struct Algoritmus<true>
{
    template<classT1, class T2> static int do_it (T1, T2) { /*...*/ }
};

// Instanciace 'elaborate' bude automaticky instanciovat správný způsob jak pracovat.
template<classT1, class T2>
int elaborate (T1 A, T2 B)
{
    // Druhý způsob použít, pouze pokud 'T1' je celé číslo a 'T2' číslo
    // s pohyblivou řádovou čárkou, jinak použít první způsob.
    return Algorithm<std::is_integral<t1>::value && std::is_floating_point<t2>::value>::do_it( A, B ) ;
}

Pomocí typových rysů definovaných v hlavičkovém souboru <type_traits> je také možné vytvářet operace pro transformace typů (static_cast a const_cast jsou uvnitř šablony nedostatečné).

Tento typ programování vytváří elegantní a stručný kód; slabinou těchto techniky však je ladění: je nepříjemné při překladu a velmi obtížné při provádění programu.

Uniformní metoda výpočtu návratového typu funkčních objektů

editovat

Určování návratového typu objektu šablony funkce v době překladu není intuitivní. Zejména pokud návratová hodnota závisí na parametrech funkce. Jako příklad:

struct Clear
{
    int operator()(int) const;       // Typ parametru je
    double operator()(double) const; // stejný jako návratový typ.
};

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

Instanciací šablony třídy Calculus<clear>, funkce objekt of calculus bude mít vždy stejný návratový typ jako funkční objekt struktury Clear. Je-li však dána třída Confused níže:

struct Confused
{
    double operator()(int) const; // Typ parametru není
    int operator()(double) const; // roven návratovému typu.
};

Snaha instanciovat Calculus<confused> způsobí, že návratový typ struktury Calculus nebude stejný jako typ třídy Confused. Překladač může generovat varování o konverzi z int na double a naopak.

TR1 představilo a C++11 přejalo, šablonovou třídu std::result_of, která umožňuje určit a používat návratový typ objektu funkce v deklaracích. Objekt CalculusVer2 používá objekt std::result_of pro zjištění 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;
};

Díky tomu v instancích funkčního objektu CalculusVer2<confused> nejsou žádné konverze, varování ani chyby.

Jedinou změnou oproti verzi std::result_of z TR1 je, že TR1 verze umožňovala, aby implementace selhala při určování typu výsledku volání funkce. Kvůli změnám v C++ pro podporu decltype, std::result_of v C++11 již tyto speciální případy nepotřebuje; implementace musí být schopny odvodit typ ve všech případech.

Vylepšení kompatibility s jazykem C

editovat

Pro kompatibilitu s jazykem C podle normy C99 byly přidány tyto vlastnosti:[17]

  • Preprocessor:[18]
    • variadická makra,
    • zřetězení sousedících úzkých nebo širokých řetězcových literálů,
    • _Pragma() – ekvivalent direktivy #pragma.
  • long long – celočíselný typ s velikostí nejméně 64 bitů
  • __func__ – makro, které se vyhodnocuje na jméno funkce, ve které je použito.
  • Hlavičkové soubory:
    • cstdbool (stdbool.h),
    • cstdint (stdint.h),
    • cinttypes (inttypes.h).

Vlastnosti původně plánované, ale odstraněné nebo nezahrnuté

editovat

Hlavičky pro zvláštní TR:

  • Moduly
  • Desítkové typy
  • Speciální matematické funkce

Odloženo:

  • Koncepty
  • Úplnější nebo vyžadované podpora garbage kolektoru
  • Reflexe
  • Oblasti platnosti maker

Odstraněné a nedoporučované vlastnosti

editovat

Termín sekvenční bod byl odstraněn. Je nahrazen stanovením, že jedna operace je sekvencována před jinou nebo že dvě operace nejsou sekvencované.[19]

Původní použití klíčového slova export bylo odstraněno.[20] Slovo export však zůstává klíčovým slovem, je rezervované pro případné další použití.

Dynamická specifikace výjimek je nedoporučovaná.[20] Specifikace funkcí, které nevyhazují výjimky, je v době překladu dostupná pomocí klíčového slova noexcept, které je užitečné pro optimalizaci.

std::auto_ptr je není nedoporučované, bylo nahrazeno std::unique_ptr.

Základní třídy funkčních objektů (std::unary_function, std::binary_function), adaptéry na ukazatele na funkce, adaptéry na ukazatele na členy a binder třídy jsou nyní nedoporučované.

Reference

editovat

V tomto článku byl použit překlad textu z článku C++11 na anglické Wikipedii.

  1. a b STROUSTRUP, Bjarne. C++11 FAQ [online]. stroustrup.com [cit. 2014-10-15]. Dostupné v archivu pořízeném z originálu dne 2018-10-06. 
  2. ISO/IEC 14882:2011 [online]. [cit. 2011-09-03]. Dostupné online. 
  3. cppreference.com. en.cppreference.com [online]. [cit. 2021-09-15]. Dostupné online. 
  4. Recent milestones: C++17 published, C++20 underway. isocpp.org [online]. [cit. 2018-07-20]. Dostupné online. (anglicky) 
  5. a b C++11 Overview: What specific design goals guided the committee? [online]. Standard C++ [cit. 2015-09-04]. Dostupné v archivu pořízeném z originálu dne 2019-01-31. 
  6. Decltype (revision 5) [online]. [cit. 2022-02-16]. Dostupné v archivu pořízeném z originálu dne 2022-02-14. 
  7. auto specifier (since C++11) - cppreference.com [online]. en.cppreference.com [cit. 2016-10-18]. Dostupné v archivu pořízeném z originálu dne 2016-10-20. 
  8. GUSTEDT, Jens. Introduce the nullptr constant - v1. ISO JTC1/SC22/WG14 Document Register. International Organization for Standardization, 2019-07-09. Dostupné v archivu pořízeném z originálu dne 2020-07-27. 
  9. Tímto vznikl konflikt s navrhovaným použitím (obvyklým v ostatních jazycích) podtržítka pro seskupování číslic v numerických literálech jako celočíselných literálech, takže C++14 pro tento účel používá apostrof (jako horní čárku) pro seskupování.Daveed Vandevoorde. N3448: Painless Digit Separation [online]. 2012-09-21 [cit. 2015-08-13]. Dostupné v archivu pořízeném z originálu dne 2015-08-11. , Lawrence Crowl. N3499: Digit Separators [online]. 2012-12-19 [cit. 2015-08-13]. Dostupné v archivu pořízeném z originálu dne 2015-08-11. 
  10. ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages – C++ §3.2 One definition rule [basic.def.odr] para. 3
  11. a b Defaulted and Deleted Functions – ISO/IEC JTC1 SC22 WG21 N2210 = 07-0070 – 2007-03-11 [online]. [cit. 2012-12-20]. Dostupné v archivu pořízeném z originálu dne 2012-08-19. 
  12. Using the GNU Compiler Collection (GCC): Long Long [online]. gcc.gnu.org [cit. 2016-07-25]. Dostupné v archivu pořízeném z originálu dne 2016-08-21. 
  13. Data Type Ranges (C++) [online]. [cit. 2009-04-23]. Dostupné v archivu pořízeném z originálu dne 2009-02-21. 
  14. Samuel P. Harbison III, Guy L. Steele Jr.: "C – A Reference Manual", 5th edition, p.251
  15. MILEWSKI, Bartosz. Broken promises–C++0x futures [online]. 2009-03-03 [cit. 2010-01-24]. Dostupné v archivu pořízeném z originálu dne 2011-09-16. 
  16. C++ Regular expressions library [online]. cppreference.com [cit. 2022-12-10]. Dostupné online. 
  17. Clang - C++98, C++11, and C++14 Status [online]. Clang.llvm.org, 2013-05-12 [cit. 2013-06-10]. Dostupné v archivu pořízeném z originálu dne 2019-05-28. 
  18. Working draft changes for C99 preprocessor synchronization [online]. www.open-std.org [cit. 2014-05-26]. Dostupné v archivu pořízeném z originálu dne 2020-07-31. 
  19. CAVES, Jonathan. Update on the C++-0x Language Standard [online]. 2007-06-04 [cit. 2010-05-25]. Dostupné v archivu pořízeném z originálu dne 2011-09-09. 
  20. a b SUTTER, Herb. Trip Report: March 2010 ISO C++ Standards Meeting [online]. 2010-03-03 [cit. 2010-03-24]. Dostupné v archivu pořízeném z originálu dne 2018-07-11. 

Související články

editovat

Externí odkazy

editovat