V příspěvku bych chtěl popsat některé novinky v C++11 a C++14, ke kterým jsem se nedostal ve svých předchozích příspěvcích Co je nového v C++11 a Co je nového v C++14.
C++11
Začněme C++11. Pak přejdeme na C++14.
Referenční kvalifikátory (reference qualifiers)
Je všeobecně známé, že pomocí const
kvalifikátoru jste schopni rozlišit, zda je metoda volána na konstantním či nekonstantním objektu. Příklad z std::vector
:
iterator begin(); // (1) const_iterator begin() const; // (2)
Pokud tedy zavoláte begin()
na konstantním vektoru, zavolá se metoda (2), jinak se zavolá metoda (1). Nic nového.
C++11 spolu s r-hodnotovými referencemi přináší i možnost rozlišení, zda je metoda volána na l-hodnotě či r-hodnotě pomocí tzv. referenčních kvalifikátorů &
a &&
:
class A { public: void foo() & { std::cout << "foo() &"; } void foo() && { std::cout << "foo() &&"; } }; A a; a.foo(); // Vypíše "foo() &", protože 'a' je l-hodnota. A().foo(); // Vypíše "foo() &&", protože 'A()' je r-hodnota.
Inline prostory jmen (inline namespaces)
C++ zavádí tzv. inline prostory jmen, jež automaticky zpřístupní všechny své členy v obalujícím (vnějším) prostoru jmen.
namespace A { inline namespace B { class C {}; } } A::B::C c1; // OK (očekávaně). A::C c2; // Taktéž OK, protože B je inline.
Tento mechanismus umožňuje např. verzování knihoven na úrovni jazyka.
Externí šablony (extern templates)
Pokud překladač v C++98 narazí na plně specifikovanou šablonu (např. std::vector<int> v;
), musí provést její instanciaci do aktuálně překládaného modulu (angl. translation unit). To zbytečně prodlužuje překlad v situacích, kdy je stejná šablona se stejnými typy použita ve více modulech. Dále to komplikuje linkování, protože linker by měl duplicitní instanciace odstranit, aby výsledný program zbytečně neobsahoval duplicitní kód.
C++11 umožňuje s využitím klíčového slova extern
říct, že daná šablona bude instanciována jinde (v jiném modulu):
extern template class std::vector<int>;
V některém z překládaných modulů pak musí být explicitní instanciace dané šablony:
template class std::vector<int>;
Připomíná to externí proměnné z C či C++ (význam je podobný, včetně stejného klíčového slova).
Atributy (standardized attribute syntax)
C++11 zavádí standardní zápis pro atributy. Dříve si každý překladač řešil specifikaci atributů po svém (např. __attribute__
u GCC). Standardizovaný zápis má tvar [[atribut(parametry)]]
(parametry jsou volitelné). Zároveň zavádí dva standardní atributy:
[[noreturn]]
: Indikuje, že daná funkce nevrací řízení zpět volajícímu. O takových funkcích jsem zde už psal. Příklad:
[[noreturn]] void my_exit(int exit_code) { std::cerr << "exiting with " << exit_code << "\n"; std::exit(exit_code); }
[[carries_dependency]]
: Umožňuje překladači lépe optimalizovat vícevláknový kód (vysvětlení).
C++14 pak zavádí další standardní atribut: [[deprecated]]
, o němž jsem již psal.
Dědění konstruktorů (inheriting constructors)
Mějme bázovou třídu A
:
class A { public: A(int i, int j): i(i), j(j) {} private: int i; int j; };
Pokud jste v C++98 dědili třídu B
z třídy A
a chtěli jste u B
použít stejné konstruktory, jako má A
, museli jste si delegaci napsat sami:
// C++98 class B: public A { B(int i, int j): A(i, j) {} }; // Bez definování konstruktoru v B výše by se následující kód nepřeložil. B b(1, 2);
C++11 umožňuje "dědění" konstruktorů pomocí using
:
// C++11 class B: public A { using A::A; }; B b(1, 2); // OK.
Zjednoduší to kód.
Odkazy: 0
Delegování konstruktorů (delegating constructors)
V C++98 nelze volat z konstruktoru jiný konstruktor téže třídy. C++11 to však umožňuje:
class A { public: A(): A(1) {} // Delegace. A(int i): i(i) {} private: int i; };
Nový specifikátor 'noexcept'
Pomocí specifikátoru noexcept
lze specifikovat, zda daná funkce nevyhazuje výjimky. Např. následující deklarace říká, že foo()
nikdy nevyhodí výjimku:
void foo() noexcept;
Ve standardní knihovně je takto označená např. metoda std::vector::size()
:
size_type size() const noexcept;
Tato informace je jednak užitečná pro programátory a jednak pro překladače, protože jim umožňuje generovat efektivnější kód.
Poznámky:
- V C++98 se k podobnému účelu používalo
throw()
, patřící do dynamické specifikace výjimek. Tato dynamická specifikace je ale od C++11 zavržená (angl. deprecated). - Překladač nijak nekontroluje, zda k vyvolání výjimky skutečně nemůže dojít. To je na programátorovi. Pokud by k vyvolání došlo, tak dojde k zavolání
std::terminate()
[C++11, 15.5.1], což typicky ukončí program zavolánímstd::abort()
. - Pomocí
noexcept(expr)
lze deklarovat, že daná funkce nevyhazuje výjimku pouze za určitých předpokladů, které jsou dány výrazemexpr
.
Nové operátory 'alignof' a 'alignas' (zarovnání)
Pomocí nových operátorů alignof
a alignas
se lze dotazovat na zarovnání typů v paměti a naopak zarovnání specifikovat.
// Implicitní zarovnání (). struct A { char c; int i; }; // Explicitně vyžádané zarovnání na 16 bajtů. struct alignas(16) B { char c; int i; }; std::cout << alignof(A) << "\n"; // Vytiskne implementačně závislou hodnotu (např. 4). std::cout << alignof(B) << "\n"; // Vytiskne 16.
Pro jistotu: Céčkový operátor sizeof
vrací velikost daného typu v paměti, nikoliv zarovnání.
Explicitní konverzní operátory (explicit conversion operators)
C++98 zavedlo specifikátor explicit
, pomocí kterého lze omezit použití konstruktorů beroucí jeden argument k implicitním konverzím. Bohužel, vyřešila se tím jen část problému. Pokud totiž třída definovala konverzní operátory (např. operator bool()
), tak libovolná instance dané třídy mohla být potichu implicitně konvertována na typ, který nebyl zamýšlen (např. z objektu na bool
a pak na libovolný celočíselný typ).
V C++98 se to muselo řešit oklikou, pomocí tzv. safe bool idiomu. Od C++11 však lze označit jako explicitní i konverzní operátory:
class A { public: explicit operator bool() const { return false; } }; void func(int i); A a; if (a) { /* ... */ } // OK, jedná se o bool kontext. func(a); // Chyba při překladu (nelze implicitně konvertovat a na int).
Pokud bychom onen konverzní operátor neoznačili jako explicit
, tak by volání func(a)
výše prošlo přes posloupnost konverzí a
-> bool
-> int
.
"Raw" řetězcové literály (raw string literals)
Při vytváření regulárních výrazů či vkládání kusů kódu se hodí mít možnost vytvořit řetězcový literál, kde nemusíte escapovat lomítka a uvozovky a můžete jednoduše vytvořit literál přes více řádků. To je od C++11 umožněno pomocí tzv. "raw" řetězcových literálů:
// Poznámka: Chybného obarvení syntaxe si nevšímejte. std::regex pattern(R"(\d{1,3}:"[a-d]")"); // Matchuje např. 27:"c". std::string code(R"( int main() { return 0; } )");
Funguje to tak, že všechno mezi R"(
a )"
se bere jako součást řetězce. Obecný tvar je R"xxx(...)xxx"
, kde xxx
je vámi zvolený oddělovač (klidně prázdný). To však jen pokud byste někde v řetězci potřebovali mít sekvenci )"
.
Unicode řetězcové literály (Unicode string literals)
V C++98 se pro práci s řetězci obsahující vícebajtové znaky dal použít typ wchar_t
. Jeho zásadním nedostatkem však bylo, že velikost a sémantika byla implementačně závislá. Pro přenositelnou reprezentaci Unicode (např. UTF-16) se tedy nehodil. C++11 to řeší zavedením nových typů char16_t
a char32_t
a nových standardních literálů:
const char *s1 = u8"UTF-8 řetězcový literál."; const char16_t *s2 = u"UTF-16 řetězcový literál."; const char32_t *s3 = U"UTF-32 řetězcový literál.";
Další související změnou je, že v řetězcích a znacích je nyní možné zapisovat Unicode kódové body (angl. code points) přes \uNNNN
a \UNNNNNNNN
.
Uživatelsky definované literály (user-defined literals)
V jazycích C a C++ existuje řada standardních literálů, např.
0 // Nula typu 'int' 2L // Dvojka typu 'long int' 3.0f // Trojka typu 'float'
C++11 zavádí možnost vytváření uživatelských literálů pomocí vlastních suffixů. Např. literál typu std::string
lze definovat následovně:
std::string operator "" _s(const char *str, std::size_t length) { return std::string(str, length); }
Použití vypadá takto:
std::string s1 = "abc\x00xyz"; // V s1 bude "abc". std::string s2 = "abc\x00xyz"_s; // V s2 bude "abc\x00xyz".
Všimněte si, že v prvním případě obsahuje vytvořený std::string
pouze tři znaky, protože onen literál je typu const char *
, u kterého nulový bajt značí jeho konec.
Předešlu, že v C++14 je tento literál přímo ve standardu, jen má jméno s
místo _s
(suffixy nezačínající podtržítkem jsou vyhrazené standardem, proto jsme místo s
museli použít _s
).
Neomezené unie (unrestricted unions)
C++11 snižuje omezení na typ objektů, které se mohou vyskytovat uvnitř unií. Např. je nyní možné, aby se uvnitř unie nacházel objekt s netriviálním konstruktorem:
struct Range { Range(int start, int end): start(start), end(end) {} int start; int end; }; union U { int i; Range r; // Zakázáno v C++98, OK v C++11. // Kvůli Range (má netriviální konstruktor) je potřeba definovat // konstruktor pro U. U(): r(0, 0) {} };
Rozšířené friend deklarace (extended friend declarations)
C++11 zavádí tzv. rozšířenou deklaraci přátelských tříd, kdy před názvem třídy již není potřeba používat klíčové slovo class
:
class A; class B { friend class A; // Klasická friend deklarace (OK jak v C++98, tak v C++11). friend A; // Rozšířená friend deklarace (OK od C++11). };
Výhoda je, že od C++11 lze pomocí této syntaxe deklarovat šablonové typy jako přátele:
template <typename T> class C { friend T; // Rozšířená friend deklarace (OK od C++11). };
Toto pomocí klasické deklarace (C++98) nelze zapsat:
template <typename T> class C { friend class T; // error: using template type parameter ‘T’ after ‘class’ };
Skutečně, C++98 něco takového neumožňuje. Rozšířená friend
deklarace byla zavedena právě z tohoto důvodu.
Dopředné deklarace výčtů (forward declaration of enumerators)
Výčty lze od C++11 dopředně deklarovat (angl. forward declaration). Je však u nich potřeba uvést bázový typ, což je taktéž novinka od C++11. Vypadá to takto:
enum A : short; // OK, bázový typ je short. enum B; // Chyba při překladu (chybí bázový typ). enum class C : short; // OK (tzv. silně typovaný výčet), bázový typ je short. enum class D; // OK (tzv. silně typovaný výčet), bázový typ je implicitně int.
Toto v C++98 nebylo možné, právě z důvodu, že bez seznamu prvků výčtu nebylo možno vědět, jak bude onen výčet velký (a tudíž jaký datový typ k jeho reprezentaci použít). O silně typovaných výčtech jsem psal v původním příspěvku.
Odkazy: 0
Umožnění použití sizeof na datových složkách bez nutnosti existence objektu
Mějme následující strukturu:
struct A { int i; };
Pokud byste chtěli zjistit velikost A::i
v C++98, museli byste použít objekt:
// C++98 sizeof(A::i) // Chyba při překladu: // error: invalid use of non-static data member 'i' A a; sizeof(a.i) // OK.
Od C++11 není objekt potřeba:
sizeof(A::i) // OK (C++11).
Lokální a bezejmenné typy jako parametry šablon (local and unnamed types as template arguments)
C++11 umožňuje jako parametry šablon použít i lokální či bezejmenné typy:
template <class T> void func(T t) {} // Bezejmenný typ. enum { e }; int main() { // Lokální typ. struct A { int i; }; A a; func(e); // OK (v C++98 chyba). func(a); // OK (v C++98 chyba). }
Odkazy: 0
Vylepšení kompatibility s C99
Z důvodu zlepšení kompatibility s C99 přibylo do C++11 následující:
- Typ
long long int
. Alespoň 64b celočíselný typ (samozřejmě existuje i jehounsigned
verze). Před tím byl v C++ akorátlong int
, jehož velikost byla garantována pouze 32b (mohla být samozřejmě větší, ale to nebylo z hlediska standardu zaručeno). - Identifikátor
__func__
. Název funkce, ve které je tento identifikátor použit. Hodí se to např. u ladicích výpisů. - Podpora pro makra s proměnným počtem parametrů (angl. variadic macros).
- Hlavičkové soubory. Přibyly hlavičkové soubory
<ccomplex>
,<cstdbool>
,<cstdint>
,<cinttypes>
a<ctgmath>
. - A mnoho dalších drobností...
Některé překladače však výše uvedené již dříve poskytovaly jako rozšíření.
Novinky ve standardní knihovně
Zde bohužel jen krátce, protože příspěvek už je i tak dost dlouhý. Snad se k detailnějšímu rozboru novinek standardní knihovny dostanu v budoucnu. Určitě by si to zasloužila.
Některé méně známé nové hlavičkové soubory:
<array>
: Pole o fixní velikosti (aneb Céčková pole v kontejnerovém kabátu).<atomic>
: Atomické operace.<cfenv>
: Zjišťování informací o typech s plovoucí řadovou čárkou a nastavování chování (např. zaokrouhlování).<chrono>
: Kolekce typů pro reprezentaci času.<forward_list>
: Jednosměrně vázaný seznam (std::list
je obousměrně vázaný).<initializer_list>
: Proxy objekt poskytující přístup k poli objektů (automaticky konstruován ze seznamu prvků ve složených závorkách, např. ustd::vector<int> v = {1, 2, 3};
).<random>
: Rozšiřitelné generátory a rozložení náhodných čísel. Již nikdy víc standardní Céčková funkcerand()
(motivace).<ratio>
: Přesná reprezentace racionálních čísel.<thread>
,<mutex>
,<future>
,<condition_variable>
: Standardní knihovna pro práci s vlákny.<type_traits>
: Tzv. typové rysy pro zjišťování informací o typech a jejich modifikace (např. odstranění reference).<typeindex>
: Wrapper nadstd::type_info
, který je možno používat v asociativních kontejnerech.
Některé méně známé novinky v existujících hlavičkových souborech (opravdu jen velmi malá část, novinek je hodně):
- Numerické konverze pro
std::string
(např. konverze z řetězce na číslo přesstd::stoi()
). - Snížení alokované paměti na počet prvků v kontejneru (např.
std::vector::shrink_to_fit()
). - Konstrukce prvků v kontejnerech přímo na místě (tzv. emplacement funkce, např.
std::vector::emplace_back()
). - Funkce
std::begin()
astd::end()
(fungují jak nad kontejnery, tak nad Céčkovými poli). - Nové algoritmy:
std::all_of(), std::any_of(), std::none_of()
,std::find_if_not()
,std::copy_if()
,std::copy_n()
,std::shuffle()
,std::is_sorted()
,std::minmax()
, std::is_permutation(),std::iota()
. - Polymorfní wrappery nad funkčními objekty:
std::function
, std::mem_fn. - Wrappery nad referencemi:
std::reference_wrapper
,std::ref()
. - Částečná aplikace funkcí (angl. partial function application):
std::bind()
. Doporučuji ale používat lambda funkce, protožestd::bind()
trpí řadou problémů.
C++14
Uff, u C++11 toho bylo hodně, že? Vrhněme se nyní na C++14, kde je těch novinek, které jsem dříve nezmiňoval o poznání méně.
Agregáty a inicializátory členských složek (member initializers and aggregates)
C++14 snížilo omezení z C++11 kladená na tzv. agregáty. Konkrétně je nyní umožněno, aby agregáty obsahovaly inicializátory členských složek:
struct A { int i = 0; double j = 0.0; }; A a = {1}; // OK (C++14), chyba při překladu v C++11. A b = {1, 2.0}; // OK (C++14), chyba při překladu v C++11.
Ona inicializace přes složené závorky použitá výše je klasická Céčková inicializace struktur. Pokud by vás zajímaly detaily, mrkněte na můj příspěvek na toto téma, kde je vše detailně vysvětleno.
constexpr metody nejsou implicitně const
Nadpis mluví za vše: od C++14 nejsou constexpr
metody implicitně const
:
class Point { public: constexpr Point(int x, int y): x(x), y(y) {} // V C++11 by následující metody byly implicitně const. // constexpr int getX() { return x; } // constexpr int getY() { return y; } // Od C++14 tomu tak není, takže pokud chceme mít const constexpr // metody, musíme to napsat explicitně. constexpr int getX() const { return x; } constexpr int getY() const { return y; } private: int x; int y; };
Zjednodušení zápisu transformačních typových rysů (TransformationTraits redux)
Místo některých typových rysů tvaru xxx<T>::type
lze nyní psát jen xxx_t<T>
. Příklad:
// C++11 using TWithoutRef = typename std::remove_reference<T>::type; // C++14 using TWithoutRef = std::remove_reference_t<T>;
Zpřehlední to kód.
Odkazy: 0
Heterogenní vyhledávání v asociativních kontejnerech (heterogeneous comparison lookup in associative containers)
C++14 umožňuje, aby se v kontejnerech jako je std::set
či std::map
dalo vyhledávat i podle objektů jiného typu, než je typ klíčů v daném kontejneru:
#include <functional> #include <set> struct A { explicit A(int i): i(i) {} int i; }; inline bool operator<(const A &a, const int &i) { return a.i < i; } inline bool operator<(const int &i, const A &a) { return i < a.i; } int main() { std::set<A, std::less<>> s; // std::less<> je z C++14. // ... // OK, i přesto, že 520 nelze implicitně konvertovat na A. auto it = s.find(520); if (it != s.end()) { // ... } }
Dealokace paměti s určením velikosti (sized deallocation)
C++14 zavádí možnost si nadefinovat vlastní operator delete
, který kromě ukazatele přebírá i velikost:
int *p = new int; ::operator delete(p, sizeof(int));
Samozřejmě, příklad výše je pouze ilustrativní z hlediska syntaxe.
Odkazy: 0
Zdrojové kódy
Zdrojové kódy ke všem příkladům jsou k dispozici u mě na GitHubu.
Překlep - raw string
Bych asi řekl, že kometář k raw string literals by měl být spíše takto:
Funguje to tak, že všechno mezi R"( a )" se bere jako součást řetězce. Obecný tvar je R"xxx(...)xxx", kde xxx je vámi zvolený oddělovač (klidně prázdný). To však jen pokud byste někde v řetězci potřebovali mít sekvenci )".
Re: Překlep - raw string
Ano, byl to překlep. Opraveno. Díky.
qsdc Amaravati farmers pause Padyatra over standoff with police
Xbcu Telangana: TSPSC notifies vacancies for lecturer posts in govt degree, polytechnic colleges
New Delhi: The Supreme Court on Tuesday said it would examine the constitutional validity of Section 6A of the Citizenship Act relating to illegal immigrants in Assam and commence the hearing on pleas after concluding the cases arising out of the division in the Shiv Sena. Section 6A in the Citizenship Act was inserted as a special provision to deal with the citizenship of people covered by the Assam Accord. The provision provides that those who have come to Assam on or after January 1, 1966 but before March 25, 1971 from specified hydro flask water bottle territories, including Bangladesh, as per the Citizenship Act amended in 1985, and since then are residents of Assam, must register themselves under section 18 for citizenship. As a result, the provision fixes March 25, 1971 as the cut-off date for granting citizenship to Bangladeshi migrants in Assam. A five-judge Constitution bench headed by Chief Justice D Y Chandrachud, at the outset, said that it would hear the pleas on February 14 after hearing the cases related to the Maharashtra political crisis triggered by the division in Shiv Sena. Whether Section 6A of the Citizenship Act suffers from any constitutional infirmity, hydro flask website the bench said, making it clear that this would be the main issue to be adjudicated upon and this will cover all other constitutional quest hydrojug traveler ions which may arise in the matter. :root{--slide-width:336px;--slide-height:280px;--progress-height:4px;--dot-size:10px;--inactive-dot: ccc;- Wnih Man facing sex charges, accused of causing standoff remains jailed
The future of Portsmouth Naval Shipyard is bright, with five submarines currently at the yard, all three dry docks in use for the foreseeable future, and 350 additional employees expected to be hired for 2017 鈥?adding to a shipyard workforce of 5,400 people.And with the 2008-commissioned USS New Hampshire ready for maintenance, Virginia class submarines continue to come into the yard, assuring work polene bag for the next generation of workers.That is the assessment of shipyard commander Capt. David Hunt, who spoke Friday morning to a group of about 50 people at York County Commu polene store nity Colleges Eggs and Issues breakfast in Wells, Maine. Its an exciting time at the shipyard, said Hunt, who arrived last summer to begin a tour of duty that is expected to last three years.Hunt is no stranger to Portsmouth. From 2010 to 2013, he served as an engineering and planning officer and then as an operations officer there. Most recently military deputy for shipyard operations at NAVSEA Command, he said one of the most noticeable changes since his return is the focus on workforce development. Today, when we bring people in, we give them a boot camp. This begins with classroom work and then continues at learning centers where workers learn their jobs in actual mock-ups of submarines, he said. The mock-ups are a PNSY innovation, created at the suggestion of the workers themselves. The idea is that workers learn from mis polene tasche takes in the mock-up, while on a real sub a mistake can cost time and money. Theyre