C ++ 11 - C++11
Revize jazyka C ++ |
---|
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() + 7
to 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í constexpr
funkce 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 constexpr
klíč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 constexpr
konstrukté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 constexpr
funkce. 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 constexpr
konstruktory by měl být obvykle také definován jako constexpr
konstruktor, 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.
constexpr
se liší od consteval
, zavedeného v C ++ 20 , v tom, že druhý musí vždy vytvářet časovou konstantu kompilace, přičemž constexpr
toto 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á:
- Má triviální výchozí konstruktor. To může použít výchozí syntaxi konstruktoru (
SomeConstructor() = default;
). - Má triviální kopírování a přesouvání konstruktorů, které mohou používat výchozí syntaxi.
- Má triviální operátory přiřazení kopírování a přesouvání, které mohou používat výchozí syntaxi.
- 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:
- Nemá žádné virtuální funkce
- Nemá žádné virtuální základní třídy
- Všechny jeho nestatické datové členy mají stejné řízení přístupu (veřejné, soukromé, chráněné)
- Všechny jeho nestatické datové členy, včetně všech v základních třídách, jsou v hierarchii ve stejné třídě
- Výše uvedená pravidla platí také pro všechny základní třídy a pro všechny nestatické datové členy v hierarchii tříd
- 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 memcpy
pá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 SequenceClass
sestavit 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_list
je 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_list
je 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_list
s 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_list
s čí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 var1
chová 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 var2
invokuje 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::vector
má 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::vector
který 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 auto
klíč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_type
je prostě jakýkoli, který konkrétní funkce šablony přepíše std::bind
vý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_variable
je 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 auto
v 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 decltype
lze 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ě, decltype
můž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ů.
auto
je 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 typedef
jsou s dobrým způsobem, jak snížit množství kódu.
Typ označený decltype
můž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 for
pří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 ( -> int
v tomto případě) lze vynechat, pokud všechny return
vý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 Ret
je jakýkoli přidáním typů Lhs
a Rhs
bude produkovat. I s výše uvedenou funkcí C ++ 11 decltype
to 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 lhs
a rhs
dosud 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_number
vý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 value
pomocí 5, pokud konstruktor nepřepisuje inicializaci vlastní. Výše uvedený prázdný konstruktor se tedy inicializuje value
podle 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_func
je 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 override
speciá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 override
ani final
ji 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 0
dvojí roli konstantní celé číslo a nulová konstanta ukazatele. Nejednoznačnost vlastní dvojímu významu 0
byla ř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í 0
na void *
. V důsledku toho 0
je povolena pouze jako konstanta nulového ukazatele. Toto špatně spolupracuje s přetížením funkcí :
void foo(char *);
void foo(int);
Pokud NULL
je 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_t
by 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 0
zů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 struct
je 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 == 101
udá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 Val1
není definováno, ale Enum2::Val1
je 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 explicit
klíč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 ++ bool
definová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 explicit
lze 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>;
using
Syntaxe 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 union
a 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_t
je š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 char
byla 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 char
C ++ 11 přidává dva nové typy znaků: char16_t
a 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 \u
je šestnáctkové číslo; nepotřebuje obvyklou 0x
předponu. Identifikátor \u
představuje 16bitový bod kódu Unicode; Chcete-li zadat 32bitový kódový bod, použijte \U
a 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 delimiter
můž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.5
jsou doslovné, které překladač vyřeší jako typ double
s hodnotou 12,5. Přidání přípony f
, jako v 12.5f
, však vytvoří hodnotu typu, float
která 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ý 0xA
v 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 constexpr
klíčové slovo C ++ 11, aby bylo zajištěno, že kompilátor převede doslovný text zcela v době kompilace, za předpokladu, že OutputType
jde o typ konstruovatelný a kopírovatelný pomocí constexpr a funkce doslovného zpracování je constexpr
funkce.
U numerických literálů je typ vařeného literálu buď unsigned long long
pro integrální literály, nebo long double
pro 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;
};
= delete
Specifiká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í int
bude 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ž double
ná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 int
byla velikost 64 bitů u některých populárních implementací a 32 bitů u jiných. C ++ 11 long long int
k řešení tohoto problému přidává nový celočíselný typ . Je zaručeno, že bude alespoň tak velký jako a long int
a 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 assert
a 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_assert
lze 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, false
kompilá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 long
bytosti větší než int
něco, co standard nezaručuje. Takový předpoklad platí pro většinu systémů a překladačů, ale ne pro všechny.
Povolit sizeof
práci na členech tříd bez explicitního objektu
V C ++ 03 lze sizeof
operá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 alignof
operátora zavedeného v C ++ 11.
Zarovnání objektu řízení a dotazu
C ++ 11 umožňuje vyhledávat a řídit variabilní zarovnání pomocí alignof
a alignas
.
alignof
Operá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 alignof
vrátí zarovnání odkazovaného typu; pro pole vrací zarovnání typu prvku.
alignas
Specifier ří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í #pragma
direktivy nebo klíčových slov specifických pro dodavatele (jako __attribute__
pro GNU a __declspec
Microsoft). 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 attr1
se vztahuje na typ proměnné i
, attr2
a attr3
platí pro samotnou proměnnou, attr4
se vztahuje na if
prohlášení a vendor::attr5
platí 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: noreturn
určit, že se funkce nevrací, a carries_dependency
pomoci 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_mutex
atd.) A proměnné podmínek ( std::condition_variable
a std::condition_variable_any
). Ty jsou přístupné prostřednictvím zámků Resource Acquisition Is Initialization (RAII) ( std::lock_guard
a 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_task
pro 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::async
zaří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ů proof
bez 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_pair
pro std::pair
, existuje std::make_tuple
automatické vytváření std::tuple
s pomocí dedukce typu a auto
pomáhá deklarovat takovou n -tici. std::tie
vytváří n -tice odkazů na hodnotu lvalue, které vám pomohou rozbalit n -tice. std::ignore
zde 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>::value
vrací počet prvků v řazené kolekci členůT
, -
std::tuple_element<I, T>::type
vrací typ čísla objektuI
ř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_search
se používá pro vyhledávání, zatímco pro 'hledat a nahradit' std::regex_replace
se používá funkce, která vrací nový řetězec. Algoritmy std::regex_search
a std::regex_replace
vezmou 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_ptr
a vylepšení do std::shared_ptr
a std::weak_ptr
z TR1. std::auto_ptr
je 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 rand
C 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í:
-
uniform_int_distribution
, -
uniform_real_distribution
, -
bernoulli_distribution
, -
binomial_distribution
, -
geometric_distribution
, -
negative_binomial_distribution
, -
poisson_distribution
, -
exponential_distribution
, -
gamma_distribution
, -
weibull_distribution
, -
extreme_value_distribution
, -
normal_distribution
, -
lognormal_distribution
, -
chi_squared_distribution
, -
cauchy_distribution
, -
fisher_f_distribution
, -
student_t_distribution
, -
discrete_distribution
, -
piecewise_constant_distribution
a -
piecewise_linear_distribution
.
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 ref
se používá šablona funkce (pro konstantní odkaz cref
se 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 function
byla 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_cast
a const_cast
jsou 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 calculus
bude mít vždy stejný návratový typ jako objekt funkce Clear
. Nicméně, vzhledem k Confused
níž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 Calculus
nebude stejný jako u třídy Confused
. Kompilátor může generovat varování o převodu z int
do double
a naopak.
TR1 zavádí a C ++ 11 přijímá třídu šablony, std::result_of
která umožňuje určit a použít návratový typ objektu funkce pro každou deklaraci. Objekt CalculusVer2
používá std::result_of
objekt 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_of
je, ž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 decltype
verze C ++ 11 std::result_of
již 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 export
bylo odstraněno. Klíčové slovo samotné zůstává vyhrazeno pro potenciální budoucí použití.
Specifikace dynamických výjimek jsou zastaralé. S noexcept
klíč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_ptr
je 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
- Výbor pro standardy C ++
- C ++ 0X: Nová tvář standardu C ++
- Herb Sutter blog pokrytí C ++ 11
- Pokrytí blogu Anthonyho Williamse o C ++ 11
- Beseda o C ++ 0x od Bjarne Stroustrup z University of Waterloo
- Stav jazyka: Rozhovor s Bjarne Stroustrup (15. srpna 2008)
- Stránka Wiki, která pomáhá sledovat základní jazykové funkce C ++ 0x a jejich dostupnost v překladačích
- Online reference standardní knihovny C ++ 11
- Online kompilátor C ++ 11
- Časté dotazy k C ++ 11 Bjarne Stroustrupa
- Další informace o funkcích C ++ 11: smyčka založená na rozsahu, proč je auto_ptr zastaralá atd.