David Bednárek Jakub Yaghob Filip Zavoral http://ksi.mff.cuni.cz/lectures/NPRG051/html/nprg051.html
Syntaxe ◦ range-based for, auto, type inference, function return syntax, lambda, class enum, attributes, initialization
Typy ◦ char types, raw string, unicode & userdef literals, cstdint
Knihovny ◦ numeric_limits, random, regex, locale, bitset
Později: ◦ parallelism ◦ metaprogramming
další info ◦ http://en.wikipedia.org/wiki/C++11 .. 14 .. 17 ◦ http://www.stroustrup.com
Kruliš 2014:
cyklus řízený rozsahem map<string,int> m;
C++03 for (map<string,int>::const_iterator i = m.begin(); i != m.end(); ++i) i->second ..
C++11
co je přehlednější?
for (auto&& i : m) // a lot of space for my own valuable comments :-))) i.second ..
podporované datové struktury ◦ int x[], array ◦ všechny std::knihovní kontejnery ◦ lze i vlastní kontejnery nutno implementovat: begin(), end(), iterátor s operátory * != ++
Používejte!
co použít Idiom - používejte !
& když nemáte důvod pro kopírování Pozor: kopie
C++17: for( i:x)
auto&& i : x
funguje vždy i pro proxy
const auto& i : x
chci jen prohlížet
auto& i : x
potřebuji měnit
auto i : x
opravdu potřebuji kopii
explicittype i : x
potřebuji přetypovat
kdy nepoužít ◦ jiný průchod než od začátku do konce ◦ přístup k více prvků najednou ◦ současný průchod několika kontejnery
kdy použít ◦ v (cca) 95% běžných případů
nefunguje pro proxy vector
temporalni objekt
původní význam
{
◦ lokání proměnná - storage class specifier ◦ zbytečné
}
auto int x; ..
problém ◦ zbytečná verbálnost škodí, obzvlášť když znepřehledňuje kód std::vector v = { .. }; std::vector::const_iterator i = v.begin();
◦ explicitní deklarace slouží jen ke kontrole kompilátorem ◦ případně vynucuje automatické konverze ne vždy chtěné
C++11 ◦ odvození typu proměnné z typu inicializace ◦ silně typovaný jazyk, odvození při kompilaci nejde o polymorfní typ std::vector v = { .. }; auto i = v.begin();
pozor na přesný typ ◦ C++03 typedef vector vecType; vecType v; vecType::const_iterator it = v.begin();
◦ C++11 vector v; auto it = v.begin();
v čem se liší?
pozor na přesný typ ◦ C++03 typedef vector vecType; vecType v; vecType::const_iterator it = v.begin();
const_iterator
◦ C++11 vector v; auto it = v.begin(); const vector v2; auto it2 = v2.begin();
iterator const_iterator
◦ při změně kontejneru automatická (tichá) změna odvozeného typu ◦ cbegin(), cend() vector v; auto it = v.cbegin();
pozor na přesný typ ◦ C++03 std::deque v = { .. }; for( int i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
◦ C++11 std::deque v = { .. }; for( auto&& i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
je tu nějaký problém?
pozor na přesný typ ◦ C++03 std::deque v = { .. }; for( int i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
◦ C++11 std::deque v = { .. }; for( auto&& i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
... proč?? kde??
pozor na přesný typ ◦ C++03
size_t : unsigned
std::deque v = { .. }; for( int i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
int i: 1-2 = -1
◦ C++11 std::deque v = { .. }; for( auto&& i = v.size() - 1; i > 0; i -= 2) { f( v[i], v[i-1]); }
používat opatrně když: ◦ rozhraní závisí na přesném typu ◦ odvozování ze složitějšího výrazu ◦ výpočet/korektnost závisí na přesném typu
unsigned int i: 1-2 = 4294967295
pozor na přesný typ for( auto&& i = 0; i < v.size(); ++i) ....
je tu nějaký problém?
pozor na přesný typ, na int, na 64-bit, ..., ... for( auto&& i = 0; i < v.size(); ++i) ....
int i
size_t 64-bit architektury: unsigned long long
inkrementace v rozsahu i
parametry funkcí a metod
int f( auto x) {..}
◦ kompilátor potřebuje odvodit konkrétní typ ◦ template
položky tříd ◦ a to ani inicializované ◦ možné pouze inicializované static const ale nevhodné, viz dále
class c { auto x; static auto z = 'Z'; };
syntakticky možné, ale nevhodné použití ◦ s konstantami ◦ při explicitním určení typu
auto x = 10.0; auto i = 0ul;
diskuse C++ WG
Automaticky zajištěna korektnost ◦ const_iterator
↫
const kontejner& k.begin()
Udržovatelnost ◦ změna typů (kontejnerů, šablon, ...)
Výkonnost ◦ zaručuje neprovádění žádných implicitních konverzí
Použitelnost ◦ lambdy, bindery, šablonové parametry, šablonové výrazy
Stručnost a přehlednost ◦ tradičně hlavní uváděný důvod
použití v šablonách
template void srt( T& x) { ?? tmp = x[0];
bez auto
vector< string> v; srt( v);
jaký typ tmp?
typ to musí podporovat
template void srt( T& x) { typename T::value_type tmp = x[0];
template class vector { typedef T value_type;
template void srt( T& x) { V tmp = x[0];
uživatel to musí zadat
vector< string> v; srt< string>( v);
s auto
template void srt( T& x) { auto tmp = x[0];
... samo
parametry funkcí v šablonách ◦ znám typ kontejneru ◦ neznám typ prvků
template class Srt { sort( T& x) { auto tmp = x[0]; if( lt( x[0], x[1])) ... } bool lt( ?? a, ?? b);
jaký typ parametrů?
bool lt( auto a, auto b);
nelze typedef auto V; bool lt( V a, V b);
zamyšlení ◦ jaký je vztah mezi f a g template void f( T x) { cout << x; } void g( auto x) { cout << x; }
šablona
zamyšlení ◦ jaký je vztah mezi f a g
template void f( T x) { cout << x; } void g( auto x) { cout << x; }
syntax error
◦ f šablona jakákoliv funkce s libovolným typem parametru překladač sám určí typ parametru ◦ g syntax error kompilátor potřebuje pro auto odvodit jeden konkrétní typ zde nemá z čeho
externí šablona
template bool lt(V& a,V& b) {} template class Srt { sort( T& x) { if( lt( x[0], x[1])) ... }
je to tak správně?
externí šablona
template bool lt(V& a,V& b) {} template class Srt { sort( T& x) { if( lt( x[0], x[1])) ... }
globální funkce nemá přístup k datům zabírá prostor jmen
vnořená šablona
template class Srt { template bool lt(V& a,V& b) {} sort( T& x) { if( lt( x[0], x[1])) ... }
šablona metody
◦ šablona metody vnořená v šabloně třídy ◦ stejná pravidla o přístupnosti a viditelnosti jako bez šablon
nešlo by to ještě lépe?
template class Srt { bool lt( ??(T[0])&a, ??(T[0])&b) {}
nápad: umím vytvořit
výraz požadovaného typu
decltype ◦ odvození typu z libovolného výrazu ◦ pro běžné programování typicky zbytečné ◦ užitečné v šablonách a metaprogramování int i; decltype(i) j = 5; int f( decltype(i) x) { .. }
použití template class Srt { bool lt( decltype(*&T()[0])& a, decltype(*&T()[0])& b ) {}
◦ nebo lépe template class Srt { typedef decltype(*&T()[0]) V; bool lt( V& a, V& b ) {}
odvození typu z výrazu hodnotu uměle vytvořit nevyhodnocuje se - typ declval()[0] lze použít i když nemám default constructor výrazné zpřehlednění
typedef auto V; bool lt( V a, V b);
externí šablona
template bool lt(V& a,V& b) {} template class Srt { sort( T& x) { if( lt( x[0], x[1])) ... }
vnořená šablona
template class Srt { template bool lt(V& a,V& b) {} sort( T& x) { if( lt( x[0], x[1])) ... }
šablona metody
decltype
template class Srt { bool lt( decltype(*&T()[0])& a, decltype(*&T()[0])& b ) {}
globální funkce nemá přístup k datům zabírá prostor jmen
odvození typu z výrazu nevyhodnocuje se - typ
decltype s typedef
template class Srt { typedef decltype(*&T()[0]) V; bool lt( V& a, V& b ) {}
zpřehlednění
deklarace typu návratové hodnoty na konci deklarace auto add( int x, int y) -> int;
hmm .. k čemu to je dobré ??
uvnitř class scope class { enum Type { TA, TB }; Type getType(); }; Srt::Type Srt::getType() {}
class Srt { enum Type { TA, TB }; Type getType(); }; auto Srt::getType() -> Type {}
zbytečná duplikace scope
šablony
odvození od typu hodnoty parametru
template auto doit( const Builder& b) -> decltype( b.create()) { auto val = b.create(); return val; }
šablony - kombinace parametrů T = signed char U = unsigned char
template ?? add( T t, U u);
→ int add
template decltype((*(T*)0)+(*(U*)0)) add( T t, U u);
co je přehlednější? template auto add( T t, U u) -> decltype(t+u);
o metaprogramování později variadické šablony, typelists, tuples
Najít prvek menší než... ◦ C++03 class ftor { public: ftor(int y) : y_(y) { } bool operator()(int x) const { return x
◦ C++11 auto i=find_if(v.begin(), v.end(), [=](int x){ return x
lambda
Syntaxe [ captures ] ( params )opt mutableopt -> rettypeopt { statements; }
◦ [ captures ] zpřístupnění vnějších proměnných - inicializace implicitní nebo explicitní, hodnotou nebo odkazem ◦ ( params ) parametry volání ◦ mutable lokální kopii hodnotou zpřístupněné vnější proměnné lze modifikovat ◦ -> rettype typ návratové hodnoty, nová syntaxe nepovinné - kompilátor určí z typu návratové hodnoty ◦ { statements; } what's the difference? vlastní tělo
[](){} []{}()
[ captures ] ( params ) -> rettype { statements; }
class ftor { private: CaptTypes captures_; public: ftor( CaptTypes captures ) : captures_( captures ) {} rettype operator() ( params ) { statements; } };
Capture ◦ způsob zpřístupnění vnějších entit ◦ určuje typy datových položek a konstruktoru funktoru
Explicitní capture ◦ programátor vyjmenuje všechny vnější lokální proměnné
[a,&b,this] ◦ proměnné označené & předány odkazem, ostatní hodnotou
Implicitní capture ◦ překladač sám určí vnější proměnné, capture určuje způsob předání
[=] [=,&b] ◦ předání hodnotou, vyjmenované odkazem
[&] [&,a,this] ◦ předání odkazem, vyjmenované hodnotou
kopie v okamžiku definice !
Pokus o vnořenou funkci int f( int i) { int j = i*2; int g( int k) { return j+k; } j += 4; return g( 3); }
"vnořená funkce" - nelze
Pomocí lambdy int f( int i) { int j = i*2; auto g = [?]( int k) { return j+k; }; j += 4; return g( 3); }
capture
=
(i*2)+3
&
(i*2+4)+3
capture se provádí v okamžiku definice
What-if ... Default capture by reference?? ◦ [] [&] ? function f() { int i = 0; auto fn = []{ DoWork( i); }; DoOtherWork( i); return fn; }
typ funkce: void funkce / lambda
co je zde špatně?
What-if ... Default capture by reference?? ◦ [] [&] ? function f() { int i = 0; auto fn = []{ DoWork( i); }; DoOtherWork( i); return fn; }
co je zde špatně?
Životnost ◦ volání fn po návratu z f přistupuje k neexistující proměnné
What-if ... Default capture by value?? ◦ [] [=] ? vector v = ReadBigVectorFromDisk(); auto first = find( v.begin(), v.end(), 42); auto lambda = []{ FindNext( v, first ); };
co je zde špatně?
What-if ... Default capture by value?? ◦ [] [=] ? vector v = ReadBigVectorFromDisk(); auto first = find( v.begin(), v.end(), 42); auto lambda = []{ FindNext( v, first ); };
co je zde špatně?
Efektivita ◦ hluboká kopie vektoru
Korektnost ◦ first není iterátor do v
Správné řešení ◦ [] capture nothing! ◦ programátor musí určit capture sám pak ho snad napadne... function f() { int i = 0; auto fn = [=]{ DoWork( i); }; DoOtherWork( i); return fn; } vector v = ReadBigVectorFromDisk(); auto first = find( v.begin(), v.end(), 42); auto lambda = [&v,first]{ FindNext( v, first ); };
C++14
generalized lambda capture ◦ inicializace konkrétním výrazem ◦ lze např. move ◦ C++11 pouze funktorem nebo pomocí binderů
auto ptr = make_unique(); auto lmb = [p=move(ptr)] { return p->whatever();}
Časté použití ◦ parametr v algoritmu auto it = find_if( k.begin(), k.end(), [=](int x){ x < n; }); for_each( k.begin(), k.end(), [=](int& x){ ++x; });
Jak napsat vlastní funkci? vector v; void each_even( lambada l) { auto i = v.begin(); while( i < v.end()) { l( *i); if( ++i < v.end()) ++i; } } each_even( [](int& x){ ++x; });
???
?? sum = []( int x, int y) { return x+y; };
C++11: typ lambda výrazu: ◦ is a unique, unnamed nonunion class type ◦ nelze pojmenovat typ ◦ nelze použít decltype pro deklaraci proměnné
std::function class ◦ std::function sum ◦ mírná rt režie - lambda je uvnitř objektu std::function std::function sum = []( int x, int y) { return x+y; };
auto - zcela bez režie auto sum = []( int x, int y) { return x+y; };
◦ chci uvnitř funkce zavolat kousek jiného kódu ◦ případně libovolně parametrizovaný vector v; void each_even( ??? l) { auto i = v.begin(); while( i < v.end()) { l( *i); if( ++i < v.end()) ++i; } } each_even( ???{ ++x; });
parametr funkce: kód - lambda? parametrizované vykonání kódu skutečný parametr: konkrétní kód
Šablona ◦ kompilátor si vytvoří a dosadí příslušný typ při překladu vector v; template void each_even( lambada& l) { auto i = v.begin(); while( i < v.end()) { l( *i); parametr musí if( ++i < v.end()) ++i; } odpovídat použití } each_even( [](int& x){ ++x; });
C++11 - deklarace typů parametrů
vector v; vector<shared_ptr> index;
C++11 for_each( begin(v), end(v), []( decltype(*begin(v))& x) { cout<<x; } ); sort( begin(index), end(index), []( shared_ptr const& p1, shared_ptr const& p2) { return *p1<*p2; } ); auto get_size = []( unordered_multimap<wstring, list<string>> const& m ) { return m.size(); };
šablona - neznámý typ
složitý typ
C++11 - deklarace typů parametrů C++14 - auto
vector v; vector<shared_ptr> index;
C++11
C++14
for_each( begin(v), end(v), []( decltype(*begin(v))& x) { cout<<x; } );
for_each( begin(v), end(v), []( auto& x ) { cout << x; } );
sort( begin(index), end(index), []( shared_ptr const& p1, shared_ptr const& p2) { return *p1<*p2; } );
sort( begin(index), end(index), []( auto const& p1, auto const& p2) { return *p1<*p2; } );
auto get_size = []( unordered_multimap<wstring, list<string>> const& m ) { return m.size(); };
auto get_size = []( auto const& m ) { return m.size(); };
bonus: pro jakýkoliv kontejner s size()
int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() { auto m2 = [a, b, &c]() { cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2(); }; a = 2; b = 2; c = 2; m1(); cout << a << b << c;
ujasněte si kdy přesně a jak se proměnné zpřístupňují
std::function fact; fact = [&fact](int n)->int { if(n==0) return 1; else return (n * fact(n-1)); }; cout << "factorial(4) : " << fact(4);
zkuste si namalovat vytvořenou datovou strukturu
společná funkčnost různých konstruktorů ◦ C++03 class X { X( int x) { init( x); } X( string& s) { init( stoi(s)); } init( int x) { a = x<0 ? 0 : x; } int a; };
zbytečně rozvláčné není v init listu const, performance
◦ C++11 class X { X( int x) : a( x<0 ? 0 : x) {} X( string& s) : X( stoi(s)) {} int a; };
lze přímo volat jiný konstruktor
složité výpočty v konstruktoru ◦ C++03 class Y { public: Y( int x) : a(f(x)+1), b(f(x)+2) {} private: int f( int x ) { return x*x; } int a, b; };
◦ C++11
složitá funkce vedlejší efekty
spočítá mezivýsledek zavolá správný konstruktor
class Y { public: Y( int x) : Y( x, tmp( x)) {} private: struct tmp { int fx; tmp( int x) : fx( x*x) {}}; Y( int x, tmp t) : a(t.fx+1), b(t.fx+2) {} int a, b; };
C++03 ◦ prevence proti kopírování objektů - private constr, op= ◦ automaticky vygenerované metody X::X(), X::X( const& X), X::operator=( const& X), X::~X() default constr není generován, když existuje explicitní konstruktor
C++11 ◦ lepší kontrola ◦ delete: místo chyby linkeru chyba kompilace ◦ default: možnost explicitně přidat konstruktor class X { X() = default; X( int i) { .... } X( const& X) = delete; X& operator=( const& X) = delete; };
context keyword
C++03
◦ nechtěné přetížení
C++11
v jiném kontextu může být identifikátor
◦ override ◦ musí existovat virtuální metoda základní třídy
class Base { virtual void f( double); };
class Base { virtual void f( double); };
class Derived : Base { virtual void f( int); };
class Derived : Base { virtual void f( int) override; };
Base* b = new Derived; f(0);
◦ obecně neintuitivní použití virtual u odvozené třídy
◦ final
pokud chcete použít, 3x si to rozmyslete
syntax error
class FBase final {}; class FDerived : FBase {};
class Derived : Base { virtual void f( double) override final; };
inicializace struktur a kontejnerů vector<string> v = { "xyz", "plgh", "abraca" }; vector<string> v ({ "xyz", "plgh", "abraca" }); vector<string> v { "xyz", "plgh", "abraca" }; class Person { long id; string name; int age; }; Person p = { 87654321, "My Name", 25 };
šablona initializer_list ◦ inicializace vlastních tříd ◦ ad-hoc proměnné nebo cykly
for( auto&& x : { 6, 7, 8}) .... auto y = { 9, 10, 11 }; for( auto&& i : y) ....
template class S { vector v; S( initializer_list l) : v(l) {} void append( initializer_list l) { v.insert( v.end(), l.begin(), l.end()); } }; S s = { 1, 2, 3 }; s.append( { 4, 5});
Inicializace bez nutnosti psát konstruktor ◦ odlišnosti v C++03/11/14/17
Podmínky:
struct T { int x; string s; }; T t { 1, "ahoj");
◦ pole, struct/class no private or protected non-static data members no virtual member functions C++11/14: no base classes C++17: no virtual, private, or protected base classes no user-provided constructors C++17: including those inherited from public bases explicitly defaulted or deleted constructors are allowed
C++11/14: no default member initializers
Funkčnost ◦ copy-inicializace v pořadí deklarace ◦ odvození velikosti polí s nedeklarovanou velikostí ◦ menší počet inicializátorů ➟ default inicializace zbývajících ◦ statická data se ignorují
Uniformní inicializace - koncept {} inicializace - syntaktická konstrukce
syntaxe inizializace
ttt ttt ttt ttt
x( init); y = init; z{ init}; z1 = { init};
◦ v mnoha kontextech ekvivalentní ◦ ... ale ne všude!
{} inicializace jakožto implementace uniformní inicializace ◦ použít {} ve všech možných kontextech ◦ braced initialization
iniciální hodnoty nestatických dat
nekopírovatelné objekty () - explicitní konstruktor
T { x{ 0}; y = 0; z( 0); // error!
UCO x{ 0}; UCO y( 0); UCO z = 0; // error!
eliminace implicitních narrowing conversions ◦ inicializalizační výraz není reprezentovatelný typem inicializovaného objektu zeštíhlující ☺
class int int int };
imunita proti most vexing parse ◦ cokoliv, co lze přeložit jako deklaraci, je deklarace ◦
double a, b, c; int x( a+b+c); int y = a+b+c; int z{ a+b+c}; // error!
T x( 0); T y(); // deklarace fnce T z{};
➠ Prefer braced initialization syntax
{} inicializace
vs.
initializer_list
vs.
přetěžování konstruktorů
◦ {} inicializace striktně preferuje initializer_list ◦ pokud existuje jakýkoliv způsob konverze, použije se class T { T(int i, bool b); T(int i, float f); };
class T { T(int i, bool b); T(int i, float f); T(initializer_list l); };
T T T T
T T T T
x(10, y{10, z(10, u{10,
true); true}; 5.0); 5.0};
// // // //
T(i,b) T(i,b) T(i,f) T(i,f)
x(10, y{10, z(10, u{10,
true); true}; 5.0); 5.0};
// // // //
T(i,b) T(l) T(i,f) T(l)
class T { .... T(initializer_list l); operator double() const; }; T v( x); // copy constructor T w{ x}; // T(l)
T( (float) 10, (float) true);
{} inicializace
vs.
initializer_list
vs.
přetěžování konstruktorů
◦ {} inicializace striktně preferuje initializer_list class T { T(int i, bool b); T(int i, float f); T(initializer_list l); }; T u{10, 5.0};
// error !!
class T { T(int i, bool b); T(int i, float f); T(initializer_list<string> l); }; T T T T
x(10, y{10, z(10, v{10,
true); true}; 5.0); 5.0};
// // // //
T(i,b) T(i,b) T(i,f) T(i,f)
narrowing conversion zakázáno
konverze neexistuje
Absence parametrů inicializace ⇒ default constructor ◦ prázdný seznam ⇒ initializer_list
class T { T(); T(initializer_list<string> l); }; T T T T T
x; y{} z(); u({}) v{{}}
// // // // //
T() T() deklarace! T(l) T(l)
most wexing parse
Konstruktory vectoru ◦ vector( size_t size, T init_value) ◦ vector( initializer_list l) vector x( 10, 20); vector y{ 10, 20};
nyní považováno za chybu v návrhu
// v(i,T) // v(l) !
Poučení pro autory tříd ◦ navrhovat rozhraní tak, aby nezáleželo na () a {} inicializaci ◦ pozor na rozšíření rozhraní initializer_list přebije ostatní kostruktory na mnoha místech neintuitivní chování přidání konstruktoru změní nebo zneplatní stávající kód !
Poučení pro uživatele tříd ◦ pečlivě zvažte () a {} inicializaci ◦ dobrá praxe: jeden ze způsobů jako default, druhý v případě potřeby dnes neexistuje všeobecný konsensus co je lepší
std::unique_ptr<std::unordered_map<std::string, std::string>> 40 let staré řešení: typedef ◦ typedef up> MapPtr; ◦ typedef void (*FP)(int, const string&);
C++11: using ◦ using MapPtr = up>; ◦ using FP = void (*)(int, const string&);
pro mnoho uživatelů hůře čitelné
Hlavní rozdíl: šablony / alias templates ◦ C++03: nutnost uzavřít do class / struct template using MyAllocList = std::list>; MyAllocList x; template struct MyAllocList { typedef std::list> type; }; MyAllocList::type x;
Použití v šablonách ◦ C++03: závislé jméno ⇒ nutnost typename ◦ C++11: template alias ⇒ musí to být typ template using MyAllocList = std::list>; template class C { MyAllocList x; // no typename, no ::type }
nutnost typename u závislého typu
template struct MyAllocList { typedef std::list> type; }; template class C { typename MyAllocList::type x; }
std:: typové transformace - type traits C++11
C++14
remove_const::type
remove_const_t
const T → T
remove_reference::type
remove_reference_t
T&/T&& → T
add_lvalue_reference::type
add_lvalue_reference_t
T → T&
C++03 enum ◦ ◦ ◦ ◦
"unscoped enum" slabě typovaný identifikátory hodnot globálně jednoznačné velikost určená komplátorem optimalizace paměť/výkonnost
enum Color { Red, Blue, Green } x; enum Feelings { Happy, Sad } y; int Blue; if( x < y || y < 1) ...
C++11 enum class ◦ ◦ ◦ ◦
"scoped enum" silně typovaný lokální identifikátory definovatelná velikost default: int
ColorBlue FeelingBlue
enum Color { Red, Blue, Green }; enum Feelings { Happy, Blue };
enum class E1 { Red, Blue } x; if( x == 1) ... int a = static_cast(x); enum class E2 : uint8_t { Happy, Blue }; E2 y { E2::Blue }; auto z = E2::Happy; y = blue; y = E1::blue;
scoped enum
C++11 scoped & unscoped enum ◦ lze deklarovat underlying type enum E3 : unsigned short { Xxx, Yyy }; enum class E4 : uint32_t { Xxx, Yyy };
hodnoty jsou nutné až pro implementaci
◦ forward declaration prevence totální rekompilace enum E5 : int; enum class E6; void f( E6 e);
spec u.t.
enum class E6 { Arsenal, Liverpool }; void f( E6 e) { if( e == E6::Arsenal) ....
header - deklarace
Kdy je unscoped enum vhodný
auto x = get<static_cast <size_t>(Ids::idName)>(r);
◦ implicitní konverze na integrální typ bitmask, tuple, ... enum Ids { idName, idAddress, idYear }; ◦ zbytečná verbosita using Row = tuple<string, string, int>; enum ve třídě Row r; auto x = get(r);
C/C++03 - běhové kontroly ◦ ◦ ◦ ◦
int f( char* p) { assert( p != 0); *p == ...
běhová kontrola očekávaného stavu if( false) při nepravdivé podmínce hlášení a okamžitý konec message; abort; ochrana před chybami programátorů pouze v debug módu (#define NDEBUG), v release nedělá nic
C++11 - kompilační kontroly enum color { Blue, Red, Yellow, COLORS }; string color_names[] = { "Blue", "Red", "Yellow" }; static_assert( (sizeof(color_names) / sizeof(string)) == COLORS, "colors don't match");
◦ kontrola v době kompilace, vlastní kompilační chyba ◦ parametry šablon, velikosti dat, meze polí, hodnoty konstant concepts C++14 17 20 ? stále ve vývoji
template Integral f( Intgr x, Intgr y) { static_assert( is_integral::value, "parameter must be an integral type"); }
rozšíření jazyka, vlastnosti kompilátoru, 'hinty' ◦ C/C++03: #pragma, __extkeyword
C++11: attributes ◦ [[attr]] [[ns::attr(args)]] aplikovatelný na různé části kódu ◦ význam definovaný platformou int [[attr1]] i [[attr2, attr3]]; [[attr4(arg1, arg2)]] if (cond) { ◦ neznámé atributy jsou ignorovány [[vendor::attr5]] return i; překladač, pluginy } narozdíl od C# nelze user-defined ◦ neměly by mít vliv na sémantiku
vhodné použití ◦ optimalizace, statistiky, podpora paralelismu ◦ rozhraní na vnější prostředí (serializace, databáze, síť...)
int x [[omp::shared]]; [[omp::parallel]] for(;;) [[likely(true)]] if(x==0) { .... }
class [[db::object, db::table("people"))]] person { unsigned long id [[db::id, db::type("INT")]]; string first [[db::column("first_name")]]; string last [[db::column("last_name")]]; };
[[ noreturn ]] ◦ funkce se nikdy nevrátí ◦ _Exit, abort, exit, quick_exit, unexpected, terminate, rethrow_exception, throw_with_nested, nested_exception::rethrow_nested
◦ optimalizace, warningy - dead code
[[ carries_dependency ]] ◦ atomic, memory_order - později
[[ deprecated ]] [[ deprecated("reason") ]] ◦ korektní, ale nedoporučené použití
další v C++17
[[noreturn]] void the_end() { ....whatever exit(); } obscure_type f() { if( smth_wrong) the_end(); else return obscure_type(); }
◦ fallthrough, nodiscard, maybe_unused
syntaxe: [[ vždy začátek atributu ◦
myArray[[]{ return 0; }()] = 1;
pozor na lambdy syntax error
Funkce vracející více hodnot C++03 void read( handle h, string& rs, bool& valid); read( db, s, valid); if( valid) doit( s); bool read( handle h, string& rs);
nevypadá jako funkce neintuitivní in/out par
neintuitivní rozdělení
if( read( db, s)) doit( s); pair<string, bool> read( handle h);
umělý typ, verbose
pair<string, bool> rs = read( db); if( rs.second) doit( rs.first);
neintuitivní položky
C++11 pair<string, bool> read( handle h);
pohodlnější
auto rs = read( db); if( rs.second) doit( rs.first);
ᴂᴂ read( handle h); ᴂᴂ(s,valid) = read( db); if( valid) doit( s);
mé proměnné
???
tuple, tie tuple<string,bool> read( handle h) { return make_tuple< s, b>; }
libovolný počet
rozbalení
tie(s,valid) = read( db); if( valid) doit( s); tuple<string,bool> read( handle h); auto rs = read( db); f( rs); if( get<1>(rs)) doit( get<0>(rs));
C++20 ? structured bindings
předání výsledku přístup přes položky
tuple f(....) { .... return {a,b,c}; } auto {x,y,z} = f(); auto {a,b,c} = { 1, 2, 3};
C++03
C++11
char
string
"abc"
wchar_t
wstring
L"abc"
implementačně definováno
char
string
u8"abc\xFF\xFFFF"
UTF8
char16_t
u16string
u"abc\uFFFF"
UTF16/UCS-2
char32_t
u32string
U"abc\UF0F0F0F0"
UTF32/UCS-4
pevná platformově nezávislá šířka podpora Unicode ◦ u8"" - vždy UTF8 (třeba i na ECBDIC) "žhář" ≠ u8"žhář" (default codepage, 5 vs. 8 bytes) ◦ UCS-2 starší, pevně 2-bytové ◦ UTF16 novější, nadmnožina, lze 4-bajtové ◦ UTF32 ≅ UCS-4
f = open( "C:\temp\new.txt"); ('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|
kdo nikdy neudělal podobnou chybu ?? \\ \\\\
Raw string literal ◦ ◦ ◦ ◦
nefungují escape chars všechny znaky platné (newline, tab, ", ...) možnost vlastního delimiteru lze i uR"", u8R"", ...
R" dch* ( ch* ) dch* "
R"(('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|)" "(\'(?:[^\\\\\']|\\\\.)*\'|\"(?:[^\\\\\"]|\\\\.)*\")|"
R""(A \b C)"" "\0" R"raw(GHI)raw";
"A\t\\b\nC\0GHI";
konverze string ⇝ číselný typ old C libraries #include string s = "1234"; int i = atoi( s.c_str());
C++03 istringstream ss(s); ss >> i;
C++11 int i = stoi( s);
int i = ..... ?
při neúspěchu: throw ◦ invalid_argument ◦ out_of_range
int stoi( const string& s); unsigned long stoul double stod stoull, stof, stold string to_string( int i); string to_string( double d i);
Literály definované programátorem ◦ integral number, floating-point number, character, string ◦ původní záměr: rozšiřitelnost STL ◦ užitečné pro fyzikální výpočty (CNN) Ztráta satelitu v hodnotě 125.000.000 $ byla způsobena zmatkem v jednotkách užívaných mezi týmy řídícími let. Kilograms w = 200.5_lb + 100.1_kg; Time t = 2_h + 23_m + 17_s; Year y = "MCMLXVIII"_r + 48; SomeType operator "" _suffix( const char *); SomeType operator "" _suffix( unsigned long long); SomeType operator "" _suffix( long double);
Kilograms w = 200.5_lb + 100.1_kg; Kilograms x = 200.5; // error
pouze 'speciální' konstruktor nedovolí kg = 200.5
class Kilograms { double rawWeight; public: class DoubleIsKilos{}; // a tag explicit constexpr Kilograms(DoubleIsKilos, double wgt) : rawWeight(wgt) {} };
triviální konverze constexpr Kilograms operator "" _kg( long double wgt) { return Kilograms{Kilograms::DoubleIsKilos{}, static_cast<double>(wgt)}; } constexpr Kilograms operator "" _lb( long double wgt) { return Kilograms{Kilograms::DoubleIsKilos{}, static_cast<double>(wgt*0.4535)}; }
přepočet
signed
unsigned
intmax_t
uintmax_t
int8_t int16_t int32_t int64_t int_least8_t int_least16_t int_least32_t int_least64_t int_fast8_t int_fast16_t int_fast32_t int_fast64_t
uint8_t uint16_t uint32_t uint64_t uint_least8_t uint_least16_t uint_least32_t uint_least64_t uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t
intptr_t
uintptr_t
maximální podporovaná šířka šířka přesně 8/16/32/64 bitů Optional - nemusí být podporováno
nejmenší typ dané šířky
nejrychlejší typ schopný pojmout hodnoty dané šířky
typ schopný pojmout hodnotu zkonvertovanou z void* Optional
šablona - popis vlastností aritmetických typů ◦ celočíselné, reálné ("typ s pohyblivou řádovou čárkou") #include numeric_limits::min()
min max lowest digits digits10 is_signed is_integer is_exact is_specialized radix epsilon round_error min_exponent min_exponent10 max_exponent max_exponent10 has_infinity has_quiet_NaN has_signaling_NaN has_denorm has_denorm_loss infinity quiet_NaN signaling_NaN denorm_min is_iec559 is_bounded is_modulo traps tinyness_before round_style
10 generátorů 20 distributorů
random ◦ generování náhodných čísel v požadovaném rozdělení
generators - generují rovnoměrně rozdělené hodnoty ◦ linear_congruential, mersenne_twister, substract_with_carry, ...
distributors - transformují sekvenci na konkrétní rozdělení ◦ uniform, normal, poisson, student, exponential, ...
použití: distributor( generator) #include default_random_engine generator; default_random_engine generator(time(0)); uniform_int_distribution distrib(1,6); int dice_roll = distrib(generator);
auto dice = bind( distrib, mt19937_64); auto dice = [&](){ return distrib(mt19937_64); }; int wisdom = dice()+dice()+dice();
stejná posloupnost seed
generuje 1..6
funktor generátoru s distributorm
epoch duration ◦ různé časové jednotky ◦ časová aritmetika ◦ silná typová kontrola
clock ◦ system_clock systémové real-time hodiny to_time_t(), from_time_t() - konverze z/do time_t ◦ steady_clock nikdy nejsou přeřízeny, nikdy neklesají, pravidelně rostou ◦ high_resolution_clock hodiny s nejmenším dostupným tickem
timepoint - doba od referenčního času
#include #include #include void sleep_ms( unsigned int ms) { auto t0 = chrono::high_resolution_clock::now(); this_thread::sleep_for( chrono::milliseconds(ms)); auto t1 = chrono::high_resolution_clock::now(); chrono::milliseconds total_ms = chrono::duration_cast(t1 - t0); cout << total_ms.count(); }
různé časové jednotky silná typová kontrola
chrono::seconds twentySeconds(20); chrono::hours aDay(24);
chrono::milliseconds ms; ms += twentySeconds + aDay; --ms; ms *= 2;
C++17
multiplatformní práce s adresáři a jmény souborů ◦ ... konečně - C++14 TS, C++17 std::
iterovatelný filesystem ◦ lze rekurzivně ◦ adresáře, softlinks, hardlinks, práva, atributy souborů, ... ◦ kopírování, mazání, přejmenování, ...
iterovatelné části složeného jména souboru ◦ části jména ◦ normalizované skládání dir( const string& tree) { rekurzivně adresáře fs::path treep( tree); for( auto&& de : fs::recursive_directory_iterator( treep)) { if( is_directory( de.status())) cout << ">> "; fs::path p( de ); cout << p.root_path() << ":" << p.parent_path() << ":" << p.filename() << ":" << p.stem() << ":" << p.extension() << endl; for(auto&& pi : p) cout << *pi; } části cesty }
podpora internacionalizace ◦ locale - set of facets ◦ facet [fæsit] - ploška, stránka, aspekt, charakteristika třídy use_facet < numpunct > (mylocale).decimal_point();
category
facet
member functions
porovnávání řetězců
compare, hash, transform is, narrow, scan_is, scan_not, tolower, toupper, widen ctype always_noconv, encoding, in, length, max_length, out, unshift curr_symbol, decimal_point, frac_digits, grouping, negative_sign, moneypunct neg_format, positive_sign, pos_format, thousands_sep monetary money_get get money_put put decimal_point, falsename, grouping, thousands_sep, truename numpunct get numeric num_get put num_put date_order, get_date, get_monthname, get_time, get_weekday, get_year time_get time put time_put close, get, open messages messages collate
collate ctype codecvt
C++03 void print_date( const Date& d) { switch( loc) { default: case ISO: cout << d.year() << "-" << d.month() << "/" << d.day(); break; case US: cout << d.month() << "/" << d.day() << "/" << d.year(); break; case CZ: cout << d.day() << "." << d.month() << "." << d.year(); break; }
C++11 void print_date( const Date& d) { cout.imbue( locale{"en_US.UTF-8"}); cout << d; }
lokalizace streamu
template inline bool isalpha( C c, const locale& loc) { return use_facet< ctype >(loc).is( alpha, c)); }
lokalizace třídění z<å
lokalizace vlastností znaků
locale dk{"da_DK"}; sort( v.begin(), v.end(), dk);
šablony ◦ regex regulární výraz, různé možné syntaxe ◦ match_results kontejner pro výsledky regex_search cmatch = match_results wcmatch smatch = match_results<string::const_iterator> wsmatch
funkce ◦ bool regex_match( string, regex) mnoho dalších ověří, zda celý string odpovídá regexu přetížení ◦ bool regex_search( string, smatch, regex) najde výskyt regexu v stringu a uloží ho do smatch ◦ string regex_replace( string src, regex, string fmt) v src najde všechny regex a nahradí je podle fmt, vrátí výsledek
syntaxe ◦ ECMAScript, basic, extended, awk, grep, egrep
escape regulárního výrazu #include const regex patt( R""((\+|-)?[[:digit:]]+)""); string inp = "-1234"; if( regex_match( inp, patt)) ....
const regex patt( R"(\w+day)" ); string days = "Saturday and Sunday, some Fridays."; smatch match; if( regex_search( days, match, patt ) ) { cout << match[0]; } cout << regex_replace( days, regex( "day" ), "tag" );
raw string - escape
Saturday
Saturtag and Suntag, some Fritags.
pole bitů pevné velikosti ◦ optimalizované ◦ přístup k jednotlivým bitům ◦ různé formy inicializace #include bitset<6> a(42); bitset<6> b(0x1B); bitset<18> c("100100101011101001"); string s = "BaaBBaBaaBBaB"; bitset<4> d(s, 3, 4, 'a', 'B'); a ^= b; a[2] = (~b << 2)[3]; a.set(1); odkud, kolik,
co je 0 a 1
Schválené novinky v C++17 ◦ Jacksonville 5.3.2016 ◦ oproti původním očekáváním velmi konzervativní ◦ zobecněný range-based for různé typy begin_expr a end_expr možnost např. predikátu pro ukončení cyklu musejí se umět porovnávat C++14
C++17
auto && range = range_expr; for( auto begin=begin_expr , end=end_expr ; begin!=end; ++begin) {..}
auto auto auto for(
◦ rozšířená agregátní inicializace podpora pro dědičnost
možnost ukončení predikátem
&& range = range_expr; begin = begin_expr ; end = end_expr ; ; begin!=end; ++begin) {..}
struct A { int x; }; struct B : A { double y; }; A a{2}; B b{{1},2}; // b.x=1, b.y=2 B b{{},2}; // b.x=0, b.y=2
Schválené novinky v C++17 ◦ filesystem ◦ atributy propadávání ve switchi [[fallthrough]]
warning při nepoužití návratové hodnoty [[nodiscard]] aplikovatelné na deklaraci fce nebo typu
nepoužití proměnných potlačení warningů [[maybe_unused]] void g() { [[maybe_unused]] int y; assert(y==0); }
◦ hexadecimal float exponent: mocnina 2
switch (n) { case 1: case 2: f(); [[fallthrough]]; case 3: // no warning g(); case 5: // warning h(); break; } struct [[nodiscard]] mt {}; mt f(); [[nodiscard]] int g(); f(); // warning g(); // warning
float x = 0x1C.6p+3
(1*161+12*160+6*16-1)*23
Schválené novinky v C++17 ◦ fold expressions - variadické šablony C++14 - rekurze auto old_sum(){ return 0; } template auto old_sum(T1 s, T... ts){ return s + old_sum(ts...); }
C++17 - zjednodušení zápisu template ????( T... pack) ( pack op ... ) // P1 op (P2 op ... (Pn-1 op Pn)) ( ... op pack ) // ((P1 op P2) op ... Pn-1) op Pn ( pack op ... op init ) // P1 op (P2 op ... (Pn op init)) ( init op ... op pack ) // ((init op P1) op ... Pn-1) op Pn template auto fold_sum(T... s){ return (... + s); }
C++17
template auto fold_sum1(T... s){ return (1 + ... + s); }
template void prt(Args&&... args) { (cout << ... << args) << '\n'; }
Schválené novinky v C++17 ◦ constexpr lambdas constexpr int f(int x) { return [x] { return x+10; } } auto f = [](int x) constexpr { return x; }
◦ capture *this by value kopie - kvůli paralelismu auto f = [*this](){}
// capture by copy
◦ clamping - hodnota uvnitř intervalu y = clamp( x, min, max);
◦ .... a spousta drobností, traitů, přetížení, formálních upřesnění apod.
Co jsme doufali, že bude v C++17 ◦ nebude ... možná v C++20? nebo 19? ◦ pilotní implementace, praktické zkušenosti, upřesnění ◦ concepts (TS) vlastnosti parametrů šablon ◦ modules (TS) nahrazení textových #include ◦ ranges (TS) (it, it), (it,cnt), (it,pred) založeno na concepts ◦ structured bindings auto {x,y,z} = f(); ◦ unified call syntax f(x,y) ≈ x.f(y) ◦ overloading operator. smart pointers ➟ smart references
auto concept Convertible { operator U(const T&); } template requires Convertible U convert(const T& t) { return t; } export { double g(double, int); int foo; namespace Calc { int add(int a, int b); } }
void not_exp_func(char* foo); import my_module;
... coroutines & parallelism, networking, graphics, reflection,...
... stay tuned: concurrency & parallelism metaprogramming