Méně známé novinky v C++11 a C++14

Od Petr Zemek, 2015-10-06

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.

Odkazy: 0, 1, 2, 3

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.

Odkazy: 0, 1, 2, 3

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).

Odkazy: 0, 1, 2

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.

Odkazy: 0, 1, 2

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;
};

Odkazy: 0, 1

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ím std::abort().
  • Pomocí noexcept(expr) lze deklarovat, že daná funkce nevyhazuje výjimku pouze za určitých předpokladů, které jsou dány výrazem expr.

Odkazy: 1, 2, 3

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í.

Odkazy: 0, 1, 2, 3

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.

Odkazy: 0, 1, 2

"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 )".

Odkazy: 0, 1, 2

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.

Odkazy: 0a, 0b, 1, 2

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).

Odkazy: 0, 1

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) {}
};

Odkazy: 0, 1, 2

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.

Odkazy: 0, 1

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).

Odkazy: 0, 1

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 jeho unsigned verze). Před tím byl v C++ akorát long 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í.

Odkazy: 0a, 0b, 0c, 1

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ř. u std::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á funkce rand() (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 nad std::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ě):

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.

Odkazy: 0, 1, 2

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;
};

Odkazy: 0a, 0b

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()) {
        // ...
    }
}

Odkazy: 0a, 0b, 1, 2, 3

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.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
6 + 5 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.

Jirka (neověřeno)

10 years 2 months zpět

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 )".

DennisereEnhat (neověřeno)

1 sec zpět

Pbfa Farmers Urged to Embrace Water Efficient Maize Hybrid
Share on TwitterTweetShare on LinkedInShareSend emailMailTo boost national cybersecurity capabilities, the Minis stanley deutschland try of Information, Communications, and the Digital Economy, in partnership with the Slovakian Ministry of Interior and National Administration, will host Kenya Cyber Game 2025, a 10-week initiative meant to identify and nurture promising cybersecurity professionals.The Kenya Cyber Game was launched by ICT Principal Secretary John Tanui and Slovakian Ambassador Katarina Zuffa Leligdonova on Tuesday, April 1.Principal Secretary Tanui explained that the Kenya Cyber Game, stanley cup with the theme Unlocking Your Cybersecurity Potential is scheduled to continue until June 9, 2025, and is primarily aimed at participants from tertiary institutions and those working in the cybersecurity industry. The goal of this competition is to offer university students, tertiary learners, and professionals a chance to test and improve their cybersecurity abilities, he stated.To participate, individuals must register online through the player interface. Prize eligibility is limited to Kenyan citizens; however stanley cup , non-citizens are welcome to participate without competing for prizes.The Ministry has specified that participation in the game is free of charge, and a new cybersecurity challenge, encompassing a variety of categories such as malware analysis, digital forensics, cryptography, open-source intelligence, offensive security, and security governance and processes, will be released on a weekly Mvgm New NXT North American Champion Crowned At NXT Stand 038; Deliver
Eddie Kingston is ready to fight. Kingston spoke with WrestleZone ahead of his trios match at AEW Dynasty. There, he will team up with Adam Copeland and Mark Briscoe, the man who beat him for the ROH World Title. Kingston explained how the match came up, even though he was still set to defend his title against Briscoe ahead of Dynasty. It was real simple. I didnt want鈥?I saw Mark run out. And I go, What is he doing Doesnt he know we have a match I was like, What are you doing I didnt want to see him get hurt so thats why I went out. Yeah, Adam Copeland. Hes cool. Hes nice to me in the back. Hey, whats up Bro, hey whats up Thats it. I love Chicken, Mark Briscoe to deat stanley cups uk h, Kingston said. So all right. You want to do six-man Team Match], you want to put me in a pay-per-view Okay. Thats what it is dawg. me pay the bills]. Good to go Kingston says he wants to be the world champion at the end of the day, but hes still going to have fun at Dynasty stanley tumblers because hell be in a fight. Now, the little bit of the ego I have inside me, would I want to be in a singles match for the World Title Would I want to be in Swerves position against Joe Yeah. But guess what, I dont book the show, Kingston pointed out. And this is what they want to do and we are going to do it. And Im going to go in there Im gonna chop Brody , Im gonna ki stanley fr ck Buddy . Im gonna sling on Aleister Black and see whats up. We are gonna have a good time. Win or lose Im gonna have a