Co je nového v C++14

Od Petr Zemek, 2014-09-20

V srpnu tohoto roku byl schválen nový standard jazyka C++, označovaný jako C++14. Pojďme se společně podívat, co je v něm nového oproti C++11.

Poznámka na začátek: Tento příspěvek de facto navazuje na můj předchozí příspěvek o novinkách v C++11. Budu se tedy zabývat pouze tím, co je v C++14 nového oproti C++11 a předpokládám, že C++11 znáte.

Kde jsme a kam směřujeme

Předchozím standardem je C++11, které bylo schváleno v srpnu roku 2011 a nahradilo C++98. Tento standard je nahrazen právě schváleným standardem C++14, který byl taktéž někdy označován jako C++1y. Poslední revizi draftu lze stáhnout zde. Oproti dá se říct revolučnímu C++11, které přineslo řadu novinek, přináší C++14 spíše drobnější vylepšení a řadu oprav. Dalším větším standardem (z pohledu novinek) bude C++1z, které je plánováno na rok 2017.

Co je nového v C++14

Pojďme se tedy podívat, co je v C++14 nového.

Rozšířená dedukce návratového typu funkcí

C++11 umožňovalo automatickou dedukci návratového typu u jednoduchých lambda funkcí:

// C++11
[](int x, int y) -> int { return x + y; }  // Explicitní specifikace návratového typu.
[](int x, int y)        { return x + y; }  // Automatická dedukce návratového typu.

C++14 jde ještě dál a rozšiřuje možnost dedukce návratového typu na (1) složitější lambda funkce obsahující více příkazů return, (2) klasické (ne-lambda) funkce a dokonce i na (3) rekurzivní funkce:

// C++14
auto factorial(int n) {
    if (n == 0) {
        return 1;
    }
    return n * factorial(n - 1);
}

U rekurzivních funkcí je však podmínka, že před rekurzivním voláním se musí nacházet příkaz return.

Odkazy: 0, 1, 2, 3, 4.

Generické lambda funkce

V C++11 je u lambda funkcí potřeba vždy explicitně deklarovat konkrétní typ parametrů:

// C++11
auto addInt    = [](int    x, int    y) { return x + y; }
auto addDouble = [](double x, double y) { return x + y; }
...

C++14 od tohoto požadavku upouští a umožňuje tak psát tzv. generické lambda funkce:

// C++14
auto add = [](auto x, auto y) { return x + y; }

To se hodí, protože šablonové lambda funkce nejsou podporovány.

Odkazy: 0, 1, 2, 3.

Zobecněné lambda captures

V C++11 lze specifikovat, zda se proměnné z vnějšího rozsahu (angl. outer scope) zachytí (angl. capture) hodnotou ([=var]) či referencí ([&var]):

// C++11
int total = 0;
std::for_each(begin(container), end(container), [&total](int x) {
    //                                           ^^^^^^
    // Výše je potřeba použít '&', jinak by se změny neprojevily do volající funkce.
    total += x;
});

Nelze však jednoduše specifikovat zachycení proměnných, jejichž typ umožňuje pouze přesun (angl. move). Jedná se např. o proměnné typu std::unique_ptr. C++14 toto umožňuje tak, že zavádí možnost definování lokálních proměnných v tzv. capture části lambda funkcí, které lze inicializovat libovolným výrazem. Toho lze využít např. právě pro zachycení přesunem (angl. capture by move):

std::unique_ptr<T> ptr(new T());
auto lambda = [value = std::move(ptr)] { return *value; };

Odkazy: 0, 1, 2.

Snížení omezení kladených na constexpr

C++11 zavedlo nové klíčové slovo constexpr, pomocí kterého lze označit konstantní výrazy či funkce vyčíslitelné za překladu. Na takové funkce ale C++11 klade řadu omezení. C++14 tato omezení snižuje, a to tak, že umožňuje, aby se v constexpr funkcích vyskytovaly

  • lokální proměnné,
  • příkazy if, switch, for, while a do-while,
  • výrazy, které modifikují objekty, jejichž doba životnosti započala uvnitř constexpr funkce.

Nyní lze tedy napsat např. takovouto constexpr funkci (převzato s jistými modifikacemi odtud):

// C++14
constexpr int my_strcmp(const char *str1, const char *str2) {
    int i = 0;
    while (str1[i] && str2[i] && str1[i] == str2[i]) {
        i++;
    }
    if (str1[i] == str2[i]) return 0;
    if (str1[i] < str2[i]) return -1;
    return 1;
}

Odkazy: 0, 1, 2.

Šablonové proměnné

V C++11 lze definovat šablony funkcí, tříd a typových aliasů. C++14 zavádí možnost vytváření šablon proměnných. Ukázka (převzato odtud):

// C++14
template<typename T>
constexpr T pi = T(3.14159265358979323846); // Přesnost je dána šablonovým parametrem.
 
// Použití:
template<typename T>
T area_of_circle_with_radius(T r) {
	return pi<T> * r * r;
}

Odkazy: 0, 1, 2.

Binární literály

Od C++14 jsou k dispozici celočíselné literály zapsané v binární podobě. Ukázka:

// C++14
auto a = 0b101010; // 42

Dříve šlo celočíselné literály zapisovat pouze oktalově (052), decimálně (42) či hexadecimálně (0x2a).

Odkazy: 0, 1, 2.

Oddělovač skupin číslic

V C++14 lze použít jednoduchou uvozovku (') nejen pro zápis znakového literálu ('a'), ale i jako oddělovač skupin číslic číselných literálech. Příklad:

// C++14
auto million = 1'000'000;

Na funkcionalitu to nemá žádný vliv. Jedná se pouze o zčitelnění zápisu. Sranda je, když si otevřete zdroják obsahující 1'000 v editoru, který nepodporuje C++14; obarví se vám vše za první uvozovkou :).

Tento zápis lze použít i např. u binárních literálů pro oddělení jednotlivých nibblů:

// C++14
0b1000'0001'1000'0000

Odkazy: 0, 1.

Atribut [[deprecated]]

Většina překladačů poskytuje možnost označit proměnnou, funkci atd. jako zastaralou (angl. deprecated). Např. __attribute__((deprecated)) void f(); pro GCC. Je ji stále možno použít, ale při jejím použití vypíše překladač varování. C++14 zavádí přenositelnou variantu: atribut [[deprecated]]. Příklad:

// C++14
[[deprecated]]
void f();

Ukázka varování při zavolání dané funkce:

file.cpp:6:2: warning: 'f' is deprecated [-Wdeprecated-declarations]
        f();
        ^
file.cpp:3:6: note: 'f' has been explicitly marked deprecated here
void f();
     ^

Volitelně lze specifikovat vlastní zprávu, která se zobrazí uživateli, který tuto funkci použije:

// C++14
[[deprecated("Superseded by g().")]]
void f();

Pokud by vás zajímaly další atributy, které lze v přenositelném C++ kódu použít, mrkněte zde.

Odkazy: 0, 1, 3, 4.

decltype(auto)

C++14 zavádí možnost použít decltype(auto) místo decltype(expr) pro zkrácení zápisu. Příklad (převzat odtud):

// C++11
decltype(longAndComplexInitializingExpression) var = longAndComplexInitializingExpression;
 
// C++14
decltype(auto) var = longAndComplexInitializingExpression;

Možná se ptáte, proč nepoužít pouze auto. Důvod je ten, že auto a decltype() se v některých případech chovají mírně odlišeně a s použitím pouze auto by ona proměnná mohla mít jiný, než požadovaný typ (detaily).

Detaily: 0, 1.

Standardní uživatelsky definované literály

C++11 zavedlo možnost vytváření uživatelsky definovaných literálů. C++14 zavádí následující nové standardní literály:

Ukázka:

// C++14
auto name = "Petr Zemek"s;
auto runtime = 30s;

Odkazy: 1.

Přístup k členům ntic pomocí typu

U ntic, zavedených v C++11, se k jednotlivým prvkům dá přistoupit přes index. C++14 umožňuje taktéž indexaci pomocí typu. Ukázka:

std::tuple<std::string, int> t("Martin", 25);
auto i = get<1>(t);   // C++11/14, i == 25
auto j = get<int>(t); // C++14,    j == 25

Samozřejmě, pokud by ntice obsahovala více členů stejného typu, tak se pro přístup k nim musí použít index.

Odkazy: 1.

Drobnosti ve standardní knihovně

Do standardní knihovny přibyly následující záležitosti:

To, že např. std::make_unique() není už v C++11, je pravděpodobně jen přehlédnutí standardizační komise.

Odkazy: 1.

Co jsem přeskočil

K některým změnám jsem se v tomto příspěvku nedostal. Konkrétně se jedná o následující záležitosti.

Aktualizace 6.10.2015: Většinu z přeskočených novinek níže jsem popsal v navazujícím příspěvku.

  • Umožnění inicializace agregátů obsahujících inicializátory datových složek. Detaily: 0.
  • Heterogenní vyhledávání v asociativních kontejnerech. Detaily: 0, 1.
  • Dealokace paměti s určením velikosti. Detaily: 0.
  • Upřesnění požadavků na alokaci paměti. Detaily: 0.
  • Upřesnění kontextově závislých konverzí. Detaily: 0.

Pokud víte o něčem dalším, co je v C++14 nového a nezmínil jsem to, určitě se ozvěte do komentáře. Výčet výše určitě nebude úplný.

Podpora v překladačích

Zde není nic neočekávaného. Clang 3.5 podporuje vše (status). V závěsu je GCC 4.9, které podporuje většinu z C++14 (status). Nejhůř je na tom tradičně Microsoft Visual Studio, které ani v nejnovějším technologickém preview CTP3 z 18.8.2014 nepodporuje ani celé C++11 a z C++14 podporuje jen tři záležitosti (status). Poslední stabilní verze (Visual Studio 2013 Update 3 z 4.8.2014) je na tom samozřejmě ještě hůře.

Závěrem

Jak je vidět, C++14 je spíše evoluce než revoluce. Pokud vám to používaný překladač dovolí, tak nevidím důvod, proč zůstávat u C++11 a nepřejít na C++14. Na větší změny si budeme muset počkat do C++1z. Možná se už konečně dočkáme konceptů :).

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
1 + 3 =
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í.

polacek (neověřeno)

10 years 10 months zpět

Jen mimochodem, "Snížení omezení kladených na constexpr" je poměrně dost práce, ale mělo by to být hotovo pro GCC 5. Tato verze by měla umět i concepts; prestože ty ještě nejsou v C++14.