Pokro čilé p rogramování v C++ (část B)

Post on 17-Mar-2016

52 views 0 download

description

Pokro čilé p rogramování v C++ (část B). David Bednárek www.ksi.mff.cuni.cz. C++ 11. C++11. Jazyk && (rvalue reference) Lambda auto Variadic templates Inicializ átory Podpora metaprogramov ání (constexpr, decltype, static _assert, ... ) ... a mnoho dal ších drobností Knihovny - PowerPoint PPT Presentation

transcript

PokroPokročilé pčilé programování v C++rogramování v C++(část B)(část B)

David Bednárek

www.ksi.mff.cuni.cz

C++C++1111

C++11Jazyk

&& (rvalue reference) Lambda auto Variadic templates Inicializátory Podpora metaprogramování (constexpr, decltype, static_assert, ...) ... a mnoho dalších drobností

Knihovny Nové kontejnery (C++ 2003 TR1) Podpora paralelismu

• Atomické operace, fence• Vlákna, thread-local storage• Synchronizační primitiva (mutex, lock, condition_variable,...)

regexp, random, ...

PokroPokročilý pohled na čilý pohled na kontejnerykontejnery

Kontejnery a iterátoryStandardní knihovna definuje

Mnoho druhů kontejnerů basic_string, vector, deque, list array, forward_list map, multimap, set, multiset unordered_map,

unordered_multimap, unordered_set, unordered_multiset

Odlišné přidávání/odebírání Shodný způsob procházení Každý kontejner definuje

Typy iterator, const_iterator Metody begin(), end()

Iterátory jsou inspirovány ukazatelovou aritmetikou

Ukazatele do pole se chovají jako iterátory

Algoritmy mohou pracovat s iterátory libovolného

kontejneru s ukazateli do pole s čímkoliv, co má potřebné

operátory

void clr( int & x){ x = 0;}std::vector< int> v;for_each( v.begin(), v.end(), clr);std::array< int, N> a;for_each( a.begin(), a.end(), clr);int p[ N];for_each( p, p + N, clr);

C++11

C++11

Kategorie iterátorůStandardní knihovna definuje

Algoritmy mohou pracovat s čímkoliv, co má potřebné operátory

Různé algoritmy mají různé nároky

Norma C++ definuje 5 kategorií iterátorů

random_access bidirectional forward output input

Kategorie určuje, které syntaktické konstrukce musí iterátor umožňovat

všechny kategorie iterator(I) /* copy constructor */ ++I, I++output *I = x, *I++ = xrandom_access, bidirectional, forward, input I1 = I2 I1 == I2, I1 != I2 I->m /* pokud existuje (*I).m */input *I /* pouze pro čtení */random_access, bidirectional, forward iterator() *I /* čtení i zápis */random_access, bidirectional --I, I--random_access I += n, I + n, n + I I -= n, I - n, I1 - I2 I[ n] I1 < I2, I1 > I2, I1 <= I2, I1 >= I2

Použití iterátorůPravidla použití iterátorů

Procházený rozsah bývá zadán intervalem [b,e)

Dereferencovat e se nesmí Přebíhat za e/před b se nesmí

template< iterator>void f( iterator b, iterator e){ ... = * b; // chyba !

for ( iterator i = e - 1; // chyba ! i >= b; -- i) // chyba ! { ... }}

Standardem definované iterátoryIterátory na kontejnerech

random_access category iterátory k vector a deque

forward category iterátory k forward_list

bidirectional category iterátory ostatních kontejnerů

reverse_iterator šablona pro otočení smyslu

bidirectional/random_access iterátoru

kontejnery mají rbegin()/rend() pozor:

k.rbegin() != reverse_iterator( k.end())

Falešné iterátory output category

back_inserter, front_inserter, inserter

std::vector a;std::copy( x.begin(), x.end(), std::back_inserter( a));

ostream_iteratorstd::ostream & o = ...;std::copy( x.begin(), x.end(), std::ostream_iterator( o));

input category istream_iterator

std::istream & i = ...;std::copy( std::istream_iterator( i), std::istream_iterator(), std::back_inserter( a));

iterator_traitsSložitější algoritmy

Potřebují znát typ, na který iterátor ukazuje, apod.Potřebují určit kategorii iterátoru

Standard definuje šablonu iterator_traits parametrizovanou typem iterátoru

Každý iterator ji musí definovatObvykle nepřímo definováním těchto typů uvnitř třídy iterátoru

•Pokud to nejde, explicitní specializací šablony

template<class Iterator> struct iterator_traits { typedef typename Iterator::difference_type

difference_type; typedef typename Iterator::value_type

value_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference

reference; typedef typename

Iterator::iterator_category iterator_category;

};

template<class T> struct iterator_traits<T*> { typedef ptrdiff_t difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; typedef random_access_iterator_tag

iterator_category;};

std::iteratorPomůcka pro vytváření vlastních iterátorů

šablona std::iterator použitelná jako předek třídy

template<class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>struct iterator { typedef T value_type; typedef Distance difference_type; typedef Pointer pointer; typedef Reference reference; typedef Category iterator_category;};

std::iteratorPříklad

my_iterator b, e;std::distance( b, e)

distance potřebuje znát iterator_category a difference_type

std::iterator_traits< my_iterator>::iterator_category iterator_traits vyřeší případ ukazatelů a přesměruje problém na samotný iterátor

my_iterator::iterator_category uživatelský iterátor dědí instanci std::iterator s vhodnými parametry

std::iterator<...>::iterator_category

Šablony tříd – závislé typyŠablony tříd (včetně těl metod) se při deklaraci kontrolují pouze částečně

•Překladač může kontrolovat jen to, co nezávisí na parametru•Některé překladače nedělají ani to

Překladač potřebuje odlišit jména typů od ostatních jmen•U jmen ve tvaru A::B to překladač někdy nedokáže•Programátor musí pomoci klíčovým slovem typename

template< typename T> class X{ typedef typename T::B U; // T::B je typ typename U::D p; // T::B::D je typ typename Y<T>::C q; // Y<T>::C je typ void f() { T::D(); } // T::D není typ}

•typename je nutné uvést před jmény typu ve tvaru A::B, kde A je závislé jméno•Závislé jméno je jméno obsahující přímo či nepřímo parametr šablony

Šablony tříd - thisPokud je předkem třídy závislé jméno

překladač pak neví, které identifikátory jsou zděděny• nedokáže realizovat přednost děděných před vnějšími

uživatel musí pomoci konstrukcí this-> nebo kvalifikovaným jménem• Pozor: Kvalifikované jméno vypíná virtuálnost volání funkce

template< typename T> class X: public T{ void f() { return m(); } // globální funkce m() void g() { return this->m(); } // metoda třídy T (nebo předka) void h() { return T::m(); } // metoda třídy T (nebo předka)}

problém lze také vyřešit pomocí usingtemplate< typename T> class X: public T{ using T::m; void h() { return m(); }}

my_iteratorIterator je obvykle šablona

template< typename T> class my_iterator: public std::iterator< std::forward_iterator_tag, T>{private: typedef std::iterator< std::forward_iterator_tag, T> base_;public: using typename base_::reference; using typename base_::pointer; reference operator*() const; pointer operator->() const; //...};

my_const_iteratorKompletní kontejner musí zvládat čtení nemodifikovatelného

kontejneru

void f( const my_container<T> & k){ for ( my_container<T>::const_iterator it = k.begin(); it != k.end; ++it) // ...}

Kontejner musí poskytovat dvojice metod ošetřující consttemplate< typename T> class my_container{public: typedef my_iterator< T> iterator; typedef my_const_iterator< T> const_iterator; iterator begin(); const_iterator begin() const; const_iterator cbegin() const; // podle vzoru C++11}

Vyžaduje dvě třídy implementující iterátor

Rvalue referenceRvalue reference

Francisco de Goya. Sv. František Borgiáš u lože umírajícího (detail). 1788.

Rvalue referenceRvalue reference

Nová typová konstrukceT &&

Lvalue reference Nový název pro starou typovou konstrukci

T &

Doplnění rvalue referencí do standardních knihoven Cíl: Zrychlit běh existujících zdrojových kódů Existující zdrojové kódy musí zůstat použitelné bez úprav Přidány nové funkce umožňující další zrychlení

Rvalue reference mění styl programování v C++ Konstrukce dříve neefektivní se stávají použitelnými Použití nových knihovních funkcí Přímé použití rvalue referencí Move semantika - umožňuje nové druhy ukazatelů

Rvalue referenceRvalue reference

Jasná motivace Odstranění nadbytečných kopírování

Srozumitelný způsob použití Kanonické tvary tříd Nové knihovní funkce

Složitá definice lvalue, xvalue, prvalue, glvalue, rvalue reference collapsing rules

Nedozírné následky Princip navržen v r. 2002 Ještě v r. 2010 odstraňována nevhodná vylepšení z návrhu normy

• Byla to všechna?

ZbyteZbytečná kopírováníčná kopírováníŘešení pomocí rvalue referencí

Řešení – operator=Princip řešení - operator=

Dvě různé situace

a = b; zde se hodnota b okopírovat musí

a = c + d; výsledek operátoru + se stane novou hodnotou a kopie principiálně není zapotřebí

C++11

Řešení – operator=Princip řešení - operator=

Dvě různé situace Potřebujeme dvě implementace operatoru =

a = b; zde se hodnota b okopírovat musí

T & T::operator=( const T & x); copy-assignment objekt b bude pouze čten

a = c + d; výsledek operátoru + se stane novou hodnotou a

T & T::operator=( T && x); move-assignment hodnota vrácená operátorem + bude přesunuta do a zdrojový pomocný objekt bude přesunem modifikován

C++11

Řešení – inicializaceTotéž platí pro inicializaci

Dvě různé situace Potřebujeme dvě implementace konstruktoru

T a = b; zde se hodnota b okopírovat musí

T::T( const T & x); copy-constructor objekt b bude pouze čten

T a = c + d; výsledek operátoru + se stane hodnotou a

T::T( T && x); move-constructor hodnota vrácená operátorem + bude přesunuta do a zdrojový pomocný objekt bude přesunem modifikován

C++11

copy/movecopy/moveSpeciální metody tříd – C++11

copy/moveSpeciální metody tříd

Copy constructorT( const T & x);

Move constructorT( T && x);

Copy assignment operatorT & operator=( const T & x);

Move assignment operatorT & operator=( T && x);

C++11

copy/movePřekladačem definované chování (default)

Copy constructorT( const T & x) = default;

aplikuje copy constructor na složkyMove constructor

T( T && x) = default; aplikuje move constructor na složky

Copy assignment operatorT & operator=( const T & x) = default;

aplikuje copy assignment operator na složkyMove assignment operator

T & operator=( T && x) = default; aplikuje move assignment operator na složky

default umožňuje vynutit defaultní chování

C++11

copy/movePodmínky automatického defaultu

Copy constructor/assignment operator pokud není explicitně deklarován move constructor ani assignment

operator• budoucí normy pravděpodobně zakážou automatický default i v

případě přítomnosti druhé copy metody nebo destruktoru

Move constructor/assignment operator pokud není deklarována žádná ze 4 copy/move metod ani

destruktor

C++11

copy/moveNejběžnější kombinace

Neškodná třída Nedeklaruje žádnou copy/move metodu ani destruktor Neobsahuje složky vyžadující zvláštní péči (ukazatele)

Složky vyžadující zvláštní péči Překladačem generované chování (default) nevyhovuje Bez podpory move (před C++11)

T( const T & x);T & operator=( const T & x);~T();

Plná podpora copy/moveT( const T & x);T( T && x);T & operator=( const T & x);T & operator=( T && x);~T();

C++11

copy/moveDalší kombinace

Nekopírovatelná třída Např. dynamicky alokované živé objekty v simulacích

T( const T & x) = delete;T & operator=( const T & x) = delete;

delete zakazuje generování překladačem Destruktor může ale nemusí být nutný

Přesouvatelná nekopírovatelná třída Např. unikátní vlastník jiného objektu (viz std::unique_ptr< U>)

T( T && x);T & operator=( T && x);~T();

Pravidla jazyka zakazují generování copy metod překladačem Destruktor typicky bývá nutný

C++11

TTřídy obsahující velká datařídy obsahující velká dataŘešení podle C++11

Matrix - Řešení s podporou moveclass Matrix {public: Matrix(); Matrix( const Matrix & x); Matrix( Matrix && x); Matrix & operator=( const Matrix & x); Matrix & operator=( Matrix && x); ~Matrix();  /* ... */ private: double * data_; std::size_t vsize_, hsize_;};

Default nevyhovuje Copy metody musejí alokovat nová data_ Destruktor musí dealokovat data_ Move metody musejí vynulovat položku data_ ve zdroji

• rvalue reference neznamená, že zdroj nebude destruován Assignment operátory musejí nějak uklidit starý obsah

C++11

Matrix – copy metody a destruktorMatrix::Matrix( const Matrix & x): data_( new double[ x.vsize_ * x.hsize_]), vsize_( x.vsize_), hsize_( x.hsize_){ std::copy( x.data_, x.data_ + vsize_ * hsize_, data_);}

Matrix & Matrix::operator=( const Matrix & x){ Matrix t( x); t.swap_with( * this); return * this;}

Trik s eliminací proměnné t v operátoru = nelze použít• Hodnotou předávaný parametr by zastínil move assignment

 Matrix::~Matrix(){ delete data_;}

Operátor delete je odolný proti nulovému ukazateli

C++11

Matrix – move metodyMatrix::Matrix( Matrix && x): data_( x.data_), vsize_( x.vsize_), hsize_( x.hsize_){ x.data_ = nullptr;}

x.data_ je nutné vynulovat – jinak je destruktor x dealokuje• nullptr je nový preferovaný způsob zápisu nulového ukazatele

Matrix & Matrix::operator=( Matrix && x){ x.swap_with( * this); return * this;}

Stará hodnota * this se dostane do x• Destruktor x ji časem zneškodní

C++11

Matrix – move metody - variantaMatrix::Matrix( Matrix && x): data_( x.data_), vsize_( x.vsize_), hsize_( x.hsize_){ x.data_ = nullptr;}

x.data_ je nutné vynulovat – jinak je destruktor x dealokuje• nullptr je nový preferovaný způsob zápisu nulového ukazatele

Matrix & Matrix::operator=( Matrix && x){ Matrix t( std::move( x)); t.swap_with( * this); return * this;}

std::move vynutí move constructor pro inicializaci t Stará hodnota * this se dostane do t

• Destruktor t ji včas zneškodní

C++11

lvalue/rvaluelvalue/rvaluePravidla

lvalue/rvalueU výrazu překladač zkoumá

Typ (po odstranění vnějších referencí) Kategorie - lvalue/rvalue

T x; T * p; T & lr; T && rr; T f(); T & lrf(); T && rrf();

Lvalue typu T K objektu je možné přistupovat opakovaně (má jméno)

• Pozor: pojmenovaná rvalue reference je lvalue!x, * p, lr, rr, lrf()

Rvalue typu T Tento výraz je jediná možnost přístupu k objektu

• Následovat bude už jen volání destruktoruf(), rrf(), std::move( x), std::move( rr)

• Číselné a znakové konstanty jsou rvalueToto je zjednodušená definice

Norma definuje lvalue, xvalue, prvalue, glvalue a rvalue

C++11

lvalue/rvalue

Pravidla předávání parametrů

Kategorie a typ výrazu

lvalue rvalue

T const T T const T

Typ parametru

T, const T OK OK OK OK

T & OK (+) --- --- ---

const T & OK (-) OK OK (-) OK (-)

T && --- --- OK (+) ---

const T && --- --- OK (+) OK (+)

OK (+) má přednost před OK (-)Preferované případy, neobvyklé případy

Platí i pro inicializaci proměnných a příkaz returnVše ostatní je jen volání funkcí a operátorů

ZjednodušenoSkutečná pravidla jsou komplikována existencí dalších konverzí

(předek-potomek, číselné konverze, uživatelské konverze, pole, funkce, ...)

C++11

TTřídy obsahující velká datařídy obsahující velká dataOperátory podle C++11

Matrix - Řešení s podporou moveclass Matrix {public: Matrix(); Matrix( const Matrix & x); Matrix( Matrix && x); Matrix & operator=( const Matrix & x); Matrix & operator=( Matrix && x); ~Matrix();  Matrix & operator+=( const Matrix & b) const; /* ... */private: double * data_; std::size_t vsize_, hsize_;};

Matrix operator+( const Matrix & a, const Matrix & b);Matrix && operator+( Matrix && a, const Matrix & b);Matrix && operator+( const Matrix & a, Matrix && b);Matrix && operator+( Matrix && a, Matrix && b);

C++11

Matrix - Řešení s podporou moveMatrix operator+( const Matrix & a, const Matrix & b){ return Matrix( a) += b;}Matrix && operator+( Matrix && a, const Matrix & b){ return std::move( a += b);}Matrix && operator+( const Matrix & a, Matrix && b){ return std::move( b += a);}Matrix && operator+( Matrix && a, Matrix && b){ return std::move( a += b);}

C++11

Podpora move ve funkcích/metodách

Parametr typu T && se vyplatí,pokud funkce dokáže využít prostor přinesený parametrem

Typicky se prostor použije pro návratovou hodnotu funkce Příklad: sčítání matic Nevyplatí se pro násobení matic

• Algoritmus násobení neumí pracovat "na místě" Nevyplatí se pro zřetězení řetězců

• Výsledek má jinou velikost

Funkce pak musí mít dvě varianty Parametr typu const T & Parametr typu T && Pokud je variabilních parametrů víc, exponenciální počet variant

• Viz "perfect forwarding"

C++11

lvalue/rvaluelvalue/rvalueNové rozhraní STL

lvalue/rvalue - STLNová implementace swap

void swap( T & a, T & b){ T tmp( std::move( a)); a = std::move( b); b = std::move( tmp);}

Není nutné specializovat swap pro uživatelské typy Postačí implementace move-metod

movestd::move( b);

Zkratka za:static_cast< T &&>( b);

C++11

lvalue/rvalue - STLCopy/move insertion

iterator insert( const_iterator p, const T & x);iterator insert( const_iterator p, T && x);

Druhá verze umožňuje efektivní přesun hodnoty Týká se i push_back/push_front

Emplacetemplate< typename ... TList>iterator emplace( const_iterator p, TList && ... plist);

Zkonstruuje objekt přímo na místě plist se předá jako parametry konstruktoru Existuje i emplace_back/emplace_front

Další optimalizace uvnitř implementace kontejnerů Realokace vektoru používá move namísto copy

C++11

Variadic templatesVariadic templates

Šablony s proměnlivým počtem parametrůHlavička šablony

s proměnlivým počtem typových argumentů

template< typename ... TList>class C { /* ... */ };

pojmenovaný parametr zastupující seznam typůlze i kombinovat s pevnými parametry

template< typename T1, int c2, typename ... TList>class D { /* ... */ };

platí i pro hlavičky parciálních specializací

template< typename T1, typename ... TList>class C< T1, TList ...> { /* ... */ };

C++11

Šablony s proměnlivým počtem parametrůtemplate< typename ... TList>

pojmenovaný parametr - seznam typůlze uvnitř šablony použít v těchto konstrukcích:

•vždy se suffixem ...typové argumenty v použití (jiné) šablony

X< TList ...> Y< int, TList ..., double>

seznam předků třídy class E : public TList ...

deklarace parametrů funkce/metody/konstruktoru void f( TList ... plist); double g( int a, TList ... b, double c);

•tím vzniká pojmenovaný parametr zastupující seznam hodnot

několik dalších okrajových případů

C++11

Šablony s proměnlivým počtem parametrůtemplate< typename ... TList> void f( TList ... plist);

pojmenovaný parametr - seznam hodnotlze uvnitř funkce použít v těchto konstrukcích:

•vždy se suffixem ...hodnotové argumenty ve volání (jiné) funkce/konstruktoru

g( plist ...) new T( a, plist ..., 7) T v( b, plist ..., 8);

inicializační sekce konstruktoru E( TList ... plist) : TList( plist) ... { }

několik dalších případů

C++11

Šablony s proměnlivým počtem parametrůtemplate< typename ... TList> void f( TList ... plist);

při použití je možno prvky seznamu obalit •suffix ... slouží jako kompilační for_each•(každý) výskyt názvu seznamu je nahrazen jeho i-tým prvkem

výsledkem jeseznam typů

X< std::pair< int, TList *> ...> class E : public U< TList> ... void f( const TList & ... plist);

seznam výrazů ve volání funkce/metody/konstruktoru g( make_pair( 1, & plist) ...); h( static_cast< TList *>( plist) ...); i( sizeof( TList) ...);

seznam inicializátorů v konstruktorudalší okrajové případy

C++11

Generická N-ticetemplate <class ... Types> class tuple {public: tuple( const Types & ...); /* black magic */};template < size_t I, class T> class tuple_element {public: typedef /* black magic */ type;};template < size_t I, class ... Types> typename tuple_element< I, tuple< Types ...> >::type & get( tuple< Types ...> & t);

použitítypedef tuple< int, double, int> my_tuple;typedef tuple_element< 1, my_tuple> alias_to_double;

my_tuple t1( 1, 2.3, 4);double v = get< 1>( t1);

C++11: <utility>

lvalue/rvaluelvalue/rvaluePerfect forwarding

Perfect forwarding

template< typename ... TList>iterator emplace( const_iterator p, TList && ... plist){ void * q = /* místo pro nový prvek */;

pointer r = new( q) value_type( plist ...);

/* ... */}

new( q) je starý trik: placement new využívá možnost napsat si vlastní alokátor s parametrem navíc

void * operator new( std::size, void * q) { return q; }

C++11

Perfect forwarding

template< typename ... TList>iterator emplace( const_iterator p, TList && ... plist){ void * q = /* místo pro nový prvek */;

pointer r = new( q) value_type( plist ...);

/* ... */}

Jak dokáže emplace předat parametry? Co když je skutečným parametrem lvalue? Proč neexistuje verze s const TList & Bylo by jich exponenciálně mnoho!

C++11

Perfect forwardingTrik: Skládání referencí

Jazyk definuje tato pravidla

Použijí se u šablon funkcí s parametrem typu T &&

template< typename T> void f( T && p);

X lv;f( lv);

Parametr typu T && lze navázat na lvalue typu X Dosadí se T = X &

f( std::move( lv)); Je-li skutečným parametrem rvalue typu X Dosadí se T = X

C++11

Perfect forwardingForwarding

template< typename T> void f( T && p){ g( p);}

X lv;f( lv);

Parametr typu T && lze navázat na lvalue typu X Dosadí se T = X &

f( std::move( lv)); Je-li skutečným parametrem rvalue typu X Dosadí se T = X Parametr p je však lvalue Do funkce g by měl být předán pomocí std::move

C++11

Perfect forwardingPerfect forwarding

template< typename T> void f( T && p){ g( std::forward< T>( p));}

std::forward< T> vrací T &&

X lv;f( lv);

T = X & skládání referencí zajistí, že std::forward< T> vrací X &

f( std::move( lv)); T = X std::forward< T> vrací X && v tomto případě se std::forward< T> chová jako std::move

C++11

Perfect forwarding

Správná implementace emplace

template< typename ... TList>iterator emplace( const_iterator p, TList && ... plist){ void * q = /* místo pro nový prvek */;

pointer r = new( q) value_type( std::forward< TList>( plist) ...);

/* ... */}

C++11

Policy class, traitsPolicy class, traits

Motivace k použití šablonSpolečná implementace příbuzných problémů

Autor šablony šetří práci sobě vector< int> vs. vector< double>

• implementace se liší pouze záměnou int/double vector< int> vs. vector< bool>

• odlišné problémy - dělení bajtů na bity• drobná odlišnost interface - problém reference na bit

vector< int> vs. vector< unique_ptr< X>>• odlišný způsob použití - copy vs. move semantika

Jednotný interface příbuzných implementací Autor šablony šetří práci autorům jiných šablon

template< typename T>T scalar_product( const vector< T> & x, const vector< T> & y);

template< typename K>typename K::value_type scalar_product( const K & x, const K & y);

Řešení nepravidelností v šablonáchŘešení příbuzných problémů s odlišnými detaily

Šablony různých jmen• Neušetří práci implementátorovi• Zkomplikuje použití uživateli• Dovoluje odlišnosti interface

Parciální/explicitní specializace šablony• Neušetří práci implementátorovi• Zvenčí nerozlišitelné• Dovoluje odlišnosti interface

Přídavné parametry šablony, policy classes• Ušetří práci implementátorovi• Odlišnosti jsou zvenku explicitně vynuceny• Odlišnost interface je obtížně dosažitelná

Automatické odvození parametrů - traits• Ušetří práci implementátorovi• Zvenčí nerozlišitelné• Odlišnost interface je obtížně dosažitelná

Řešení nepravidelností v šablonách Řešení příbuzných problémů s odlišnými detaily

Šablony různých jmentemplate< typename T> class vector { /*...*/ };template< typename T> class list { /*...*/ };

• podobnost interface dovoluje použití ve společném algoritmu

Parciální/explicitní specializace šablonytemplate< typename T> class vector { /*...*/ };template<> class vector< bool> { /*...*/ };

Přídavné parametry šablony, policy classestemplate< typename T, typename comparator> class map

{ /*...*/ comparator::cmp() /*...*/};

Automatické odvození parametrů - traitstemplate< typename T> class less;template<> class less< char *> { /*...*/ strcmp() /*...*/ };template< typename T> class map { /*...*/ less< T>::cmp() /*...*/ };

Kombinace policy classes a traitstemplate< typename T, typename comparator = less< T>> class map;

Šablony tříd - definiceŠablona je generická třída parametrizovaná libovolným

počtem formálních parametrů těchto druhů: celé číslo – uvnitř šablony se chová jako konstanta, použitelná i jako

meze polí ukazatel libovolného typu libovolný typ – deklarováno zápisem class T nebo typename T,

identifikátor formálního parametru se chová jako identifikátor typu, použitelný uvnitř šablony v libovolné deklaraci

seznam typů - deklarováno zápisem class T ... nebo typename T ...Prefix definice šablony

template< formální-parametry> lze použít před několika formami deklarací; oblastí platnosti

formálních parametrů je celá prefixovaná deklarace

Šablony funkcí

Šablona funkce je generická funkce (globální nebo metoda) prefixovaná konstrukcí template

se stejnými druhy formálních parametrů šablony jako u šablon třídtemplate< typename T, int k> // parametry šablony int f( T * p, int q); // parametry funkcetemplate< typename T, typename U> // parametry šablony int g( T * p, vector< U> q); // parametry funkce

Šablony funkcí lze volat dvěma způsoby Explicitně

f< int, 729>( a, b) Automaticky

g( a, b)• Překladač dopočte parametry šablony z typů parametrů funkce• Všechny formální argumenty šablony by měly být užity v typech

formálních parametrů funkce

Šablony s mnoha parametry vs. policy class

template< typename T, bool binary, bool cached>class File { // ... T get() { if ( binary ) // ... }};

File< char, false, true> my_file;

template< typename P>class File { // ... typename P::T get() { if ( P::binary ) // ... }};

struct my_policy { typedef char T; static const bool binary = false; static const bool cached = true;};

File< my_policy> my_file;

Policy class

template< typename P>class File { // ... typename P::T get() { if ( P::binary ) // ... }};

struct my_policy { typedef char T; static const bool binary = false; static const bool cached = true;};

File< my_policy> my_file;

Policy class (politika) Třída (class/struct) použitá

pouze jako parametr šablony Neexistuje objekt tohoto typu Obsahuje pouze

typy (typedef/vnořené třídy) statické konstanty statické funkce

Reference na typy z policy class jsou závislá jména a musejí mít prefix typename

V tomto příkladě politika slouží jako balíček vytvořený uživatelem

Zpřehlednění zápisu

Policy class

template< typename T, typename caching_policy, typename converting_policy>class File { // ... typename T get() { caching_policy::read(/*...*/); converting_policy::convert(/*...*/); }};

struct cached { static void read(/*...*/) {/*...*/}};

struct binary { static void convert(/*...*/){/*...*/}};

File< char, cached, binary> my_file;

Policy class (politika) Třída (class/struct) použitá

pouze jako parametr šablony Neexistuje objekt tohoto typu Obsahuje pouze

typy (typedef/vnořené třídy) statické konstanty statické funkce

V tomto příkladě je politika vytvořena autorem šablony

Uživatel si vybírá z předdefinovaných politik

Ekvivalence typů

template< typename P>class File { // ... typename P::T get() { if ( P::binary ) // ... }};

struct my_policy { typedef char T; static const bool binary = false; static const bool cached = true;};

File< my_policy> my_file;

struct my_policy2 { typedef char T; static const bool binary = false; static const bool cached = true;};

void f( File< my_policy2> & p);

f( my_file); // error

Při ekvivalenci typů rozhodují jména tříd/struktur obsah typových konstrukcí

včetně typedef

my_policy a my_policy2jsou různé typy, tudíž se liší iFile< my_policy>a File< my_policy2>

Využití neekvivalence typů

template< typename P>class Value { typename P::T v; // ...};

struct mass { typedef double T;};

struct energy { typedef double T;};

Value< mass> m;Value< energy> e;

e = m; // error

Silná typová kontrola

Odlišně pojmenované typy mají stejný obsah nejsou kompatibilní

Využití neekvivalence typů

template< typename P>class Value { double v; // ...};

struct mass {};

struct energy {};

Value< mass> m;Value< energy> e;

e = m; // error

Silná typová kontrola

Odlišně pojmenované třídy mají stejný obsah nejsou kompatibilní

Lze použít i třídy bez obsahu tag class

Využití ekvivalence typů

template< int kg, int m, int s>class Value { double v; // ...};

template< int kg1, int m1, int s1, int kg2, int m2, int s2>Value< kg1+kg2, m1+m2, s1+s2> operator*( const Value< kg1, m1, s1> & a, const Value< kg2, m2, s2> & b);

typedef Value< 1, 0, 0> Mass;typedef Value< 0, 1, -1> Velocity;typedef Value< 1, 2, -2> Energy;

Mass m;Velocity c;Energy e;e = m * c * c; // OK

Instance šablony Value se stejnými hodnotami číselných parametrů jsou ekvivalentní

Konstrukce typedef je transparentní

Explicitní specializace

template< int kg, int m, int s>struct Unit { static void print( ostream & os) { os << ”kg^” << kg << ”.m^” << m << ”.s^” << s; }};

template<>struct Unit< 1, 2, -2> { static void print( ostream & os) { os << ”J”; }};

Generickou definici šablony lze překrýt jinou definicí pro speciální případ

Jiná šablona pak může mírně měnit své chování

template< int kg, int m, int s>ostream & operator<<( ostream & os, const Value< kg, m, s> & x){ os << x << “ “; Unit< kg, m, s>::print( os); return os;}

Parciální specializacetemplate< class X, class Y> class C { /* základní definice */ };

Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů

předefinovat jinak, než určuje její základní definice• Parciální specializace může mít stejný, menší i větší počet formálních

parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice)

template< class T, class U, int n> class C< T[n], U[n]> { /* specializace pro dvě pole stejné velikosti */ };

Explicitní specializace template<> class C< char, int[ 8]> { /* ... */ };

Explicitní specializace šablony není šablona Podléhá trochu jiným (jednodušším) pravidlům

• Překlad se neodkládá• Těla metod se nemusí psát do hlavičkových souborů

Parciální specializaceTypická použití parciální a explicitní specializace

Výhodnější implementace ve speciálních případech Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší

Mírná změna rozhraní ve speciálních případech Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek

Modifikace chování jiné šablony - traits Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve

které je např. definována porovnávací funkceGenerické programování

Výpočty (s celými čísly a typovými konstrukcemi) při překladu

Parciální specializace - traitsModifikace chování jiné šablony - traits

Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve

které je např. definována porovnávací funkce

template< class T> struct char_traits;

template< class T> class basic_string { /* ... */ int compare( const basic_string< T> & b) const { /*...*/ char_traits< T>::compare( /* ... */) /*...*/ }};

template<> struct char_traits< char> { /* ... */ static int compare(const char* s1, const char* s2, size_t n) { return memcmp( s1, s2, n); }};

Parciální specializaceModifikace chování jiné šablony

Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony Autor specializace tak upravuje chování volající šablony

TraitsŠablony, ze kterých nejsou vytvářeny objektyObsahují pouze:

Definice typů Konstanty Statické funkce

Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např.

porovnávací funkci

Traits & policiesTraits

Šablony, ze kterých nejsou vytvářeny objektyObsahují pouze:

Definice typů Statické funkce

Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např.

porovnávací funkci

Policy classesTřídy, ze kterých obvykle nejsou vytvářeny objektyPředávány jako parametr šablonám

Defaultní hodnotou parametru často bývá šablona traitsUrčeny k definování určitého chování

Příklad: Alokační strategie

Policy class vs. traits

template< typename P, typename K>void for_each( K & data){ for( ... it = ... ) P::f( * it); }

struct policy_add { static void f(...) { ... }};

my_k data;for_each< policy_add>( data);

template< typename K>struct for_each_traits;

template< typename K>void for_each( K & data){ for( ... it = ... ) for_each_traits< K>::f( * it); }

template<>struct for_each_traits< my_k> { static void f(...) { ... }};

my_k data;for_each( data);

PolymorfismusPolymorfismusKompilační a běhový

Polymorfismus Polymorfismus

Stejný zdrojový kód v různých situacích dělá různé věciVolání stejně pojmenované funkce (operátoru) volá různá těla

Šetří práci programátora (zejména na údržbě)Kód není třeba kopírovat

Nahrazuje konstrukce if/switchKód je přehlednější a obsahuje méně chyb

PolymorfismusKompilační polymorfismus

Šablony, zejména:Funktory apod.Policy classes, traits

Běhový polymorfismusC: ukazatele na funkceC++: dědičnost a virtuální funkce

Běhový polymorfismus zdržujePoužívat pouze v nutném případě

Datové struktury obsahující objekty s různým chováním ("OOP")Komunikace s neznámými partnery (komponentové systémy)

Vše ostatní lze řešit kompilačním polymorfismem

Polymorfismus běhový a kompilačníclass functor {public: virtual void f( ...) = 0;};

void for_each( functor & x){ for( ... it = ... ) x.f( * it); // run-time binding}

class functor_add : public functor { virtual void f(...) { ... }};

for_each( functor_add());

template< typename P>void for_each( P & x){ for( ... it = ... ) x.f( * it); // compile-time binding}

struct functor_add { void f(...) { ... }};

for_each( functor_add());

Kompilační polymorfismus s objektem a bez

template< typename P>void for_each( P & x){ for( ... it = ... ) x.f( it); // metoda}

struct functor_add { void f(...) { ... }};

for_each( functor_add());

template< typename P>void for_each(){ for( ... it = ... ) P::f( it); // statická funkce}

struct policy_add { static void f(...) { ... }};

for_each< policy_add>();

Smart pointersSmart pointers

Smart pointersChytré ukazatele

Automatické uvolnění dynamicky alokovaného objektu Jinak se chovají jako T *

• Operátory *, ->• Porovnání ukazatelů, nullptr

Výlučné vlastnictví Vyžaduje move-semantiku

std::unique_ptr< T>• Varianta pro pole: Operátor [] namísto * a ->

std::unique_ptr< T[]>

Sdílené vlastnictví Založeno na počítání odkazů

std::shared_ptr< T>

Vedlejší odkaz, který dovoluje zánik sdíleného objektustd::weak_ptr< T>• Lze povýšit na shared_ptr, pokud objekt dosud nezanikl

C++11

Smart pointersVýlučné vlastnictví

{ std::unique_ptr< T> q; // obsahuje nullptr { std::unique_ptr< T> p = new T( /*...*/); q = std::move( p); } q->f(); h( * q);} // zde zaniká q i objekt T

Sdílené vlastnictví{ std::shared_ptr< T> q; // obsahuje nullptr { std::shared_ptr< T> p = std::make_shared< T>( /*...*/); q = p; // zde se zvětšuje čítač p->f(); } // zde se zmenšuje čítač { std::weak_ptr< T> r = q; h( * r); } } // zde zaniká q i objekt T

C++11

Smart pointers – použití u velkých datových typůCíl: Zabalit velká data tak,

aby se chovala jako hodnota

class BigBody { // velká data // metody};

class Big {public: // konstruktory, operátory, metodyprivate: kind_of_smart_ptr< BigBody> b_;};

Big a, b, c; a = b + c;Proč nepracujeme přímo s BigBody?

Kopírování BigBody je příliš drahé Implementace move-semantiky pro BigBody může být složitá Třída BigBody může mít virtuální funkce a potomky

C++11

Smart pointers – použití u velkých datových typůŘešení A

BigBody se kopíruje vždy, když se kopíruje Big• Každé BigBody má jediného vlastníka• Neušetříme žádné kopírování

Snadná implementace move-semantiky• Default pro move metody vyhovuje, ale je třeba jej vynutit,

protože existují copy metody• Copy metody nemají default (unique_ptr nemá copy-semantiku)

class Big {public: Big( /*...*/) : b_( new BigBody( /*...*/) {} Big( const Big & x) : b_( new BigBody( * x.b_)) {} Big & operator=( const Big & x) { return * this = Big( x); } Big( Big && x) = default; // : b_( std::move( x.b_)) {} Big & operator=( Big && x) = default; // { b_ = std::move( x.b_); } ~Big() = default; // {} Big & operator+=( const Big & x) { b_->add( x.b_.get()); return * this; }private: std::unique_ptr< BigBody> b_;};

C++11

Smart pointers – použití u velkých datových typůŘešení A – ošetření dědičnosti

Abstraktní třída AbstractBody má různé potomky• Virtuální metoda clone pro kopírování, virtuální destruktor

AbstractBody * ConcreteBody1::clone() const{ return new ConcreteBody1( * this); }

• Virtuální funkce implementující jednotné rozhraní• Nemá smysl publikovat funkce formou operátorů

Metody/operátory třídy Big nikdy nejsou virtuální!

class Big {public: Big( /*...*/) : b_( new ConcreteBody1( /*...*/) {} Big( /*...*/) : b_( new ConcreteBody2( /*...*/) {} Big( const Big & x) : b_( x.b_->clone())) {} Big & operator=( const Big & x) { return * this = Big( x); } Big( Big && x) = default; Big & operator=( Big && x) = default; ~Big() = default;private: std::unique_ptr< AbstractBody> b_;};

C++11

Smart pointers – použití u velkých datových typůŘešení B

BigBody se kopíruje, jenom když je to nutné• BigBody může být ve společném vlastnictví• Před modifikujícími operacemi je nutné BigBody privatizovat

Default pro copy/move metody vyhovuje

class Big {public: Big( /*...*/) : b_( std::make_shared< BigBody>( /*...*/)) {}

Big & operator+=( const Big & x) { if ( ! b_.unique() ) b_ = std::make_shared< BigBody>( * b_); b_->add( x.b_.get()); }private: std::shared_ptr< BigBody> b_;};

C++11

Smart pointers – typy s referenční semantikouReferenční semantika

Uživatel třídy Ref ví, že jde o odkaz na Body• Kopírování Ref nezpůsobuje kopírování Body

Default pro copy/move metody vyhovuje• Ve srovnání s Big/BigBody chybí privatizace

Proč nepoužíváme přímo shared_ptr< body>?• Na třídě Ref lze definovat hezčí rozhraní včetně operátorů

class Ref {public: Ref( /*...*/) : b_( std::make_shared< Body>( /*...*/)) {}

Ref & operator+=( const Ref & x) { b_->add( x.b_.get()); }private: std::shared_ptr< Body> b_;};

C++11

weak_ptr – nepovinné odkazyPříklad: soubory s cache

typedef std::shared_ptr< FileBody> FileRef;typedef std::weak_ptr< FileBody> WeakFileRef;

class Pool {public: FileRef open(/*...*/) { FileRef r = std::make_shared< FileBody>( this); files_.push_back( r); return r; } void panic() { for_each( files_.begin(), files_.end(), []( const WeakFileRef & fw) { FileRef fs = fw.lock(); if ( fs ) fs->release_cache(); }); }private: std::vector< WeakFileRef > files_;};

Soubor otevřený metodou open bude uzavřen (FileBody zanikne) při zániku posledního FileRef

WeakFileRef přetrvají i po zániku FileBody, nedovolují však přístup• Metoda lock konvertuje na FileRef (který může být nulový)

C++11

Generické programováníGenerické programováníVýpočty při překladu

Teoretický pohled na šablonyPřekladač dokáže vyhodnotit celočíselnou aritmetiku

I s rekurzivními funkcemi

template< int N> struct Fib { static const int value = Fib< N-1>::value + Fib< N-2>::value;};

template<> struct Fib< 0> { static const int value = 1;};template<> struct Fib< 1> { static const int value = 1;};

Kontrolní otázka:Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value

Teoretický pohled na šablonyPřekladač dokáže vyhodnotit celočíselnou aritmetiku

I s rekurzivními funkcemi

template< int N> struct Fib { static const int value = Fib< N-1>::value + Fib< N-2>::value;};

template<> struct Fib< 0> { static const int value = 1;};template<> struct Fib< 1> { static const int value = 1;};

Kontrolní otázka:Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::valueMS Visual C++ 7.1: Build Time 0:00Kompilátory ukládají již vytvořené instanciace a nepočítají je znovu

Triky s šablonamiPodmíněný výraz nad typy

template< bool C, typename A, typename B>struct conditional { typedef A type;};

template< typename A, typename B>struct conditional< false, A, B> { typedef B type;};

conditional< C, A, B>::type je typPoužití

template< bool wide>class File { typename conditional< wide, wchar_t, char>::type get(); /* ... */};

C++11: <type_traits>

Triky s šablonamiPorovnání typů s booleovským výstupem

template< class A, class B>struct is_same { static const bool value = false;};

template< class A>struct is_same< A, A> { static const bool value = true;};

is_same< X, Y>::value je konstantní výrazPoužití

template< class T1>class Test { static const bool very_long = is_same< long long, T1>::value; typedef conditional< is_long, unsigned long long, unsigned long> T; /* ... */};

C++11: <type_traits>

Triky s šablonamiKompilační ověření invariantu

template< bool x>struct static_assert { struct type {};};

template<>struct static_assert< false> {};

template< int x>struct Assert { typename static_assert< (x > 0)>::type ignore_me();};

template< int x>struct Assert { static_assert( x > 0);};

C++11: vestavěno v jazyce

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Seznam typů

template< class H, class R> struct List { typedef H Head; typedef R Rest;};

struct EmptyList {};

Použití

typedef List< char *, List< const char *, List< std::string, EmptyList> > > StringTypes;

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Seznam typů

template< class H, class R> struct List { typedef H Head; typedef R Rest;};

struct EmptyList {};

Jiné použití

struct Apple {}; struct Pear {}; struct Plum {};

typedef List< Apple, List< Pear, List< Plum, EmptyList> > > Fruits;

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Seznam typů

template< class H, class R> struct List { typedef H Head; typedef R Rest;};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L> struct First { typedef typename L::Head Result;};

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Seznam typů

template< class H, class R> struct List { typedef H Head; typedef R Rest;};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L> struct First { typedef typename L::Head Result;};

struct NullType {};template<> struct First< EmptyList> { typedef NullType Result;};

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Seznam typů

template< class H, class R> struct List { typedef H Head; typedef R Rest;};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L, int n> struct Nth { typedef typename Nth< typename L::Rest, n-1>::Result Result;};template< class L> struct Nth< L, 0> { typedef typename L::Head Result;};

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Jiná implementace seznamu typů

template< class H, class R> struct List;

struct EmptyList;

Funkce na seznamu typůtemplate< class L, int n> struct Nth;

template< class H, class R, int n> struct Nth< List< H, R>, n> { typedef typename Nth< R, n-1>::Result Result;};template< class H, class R> struct Nth< List< H, R>, 0> { typedef H Result;};

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Moderní implementace seznamu typů

template< typename ... L> struct tuple;

Funkce na seznamu typů

template< size_t n, typedef X> struct tuple_element;

template< size_t n, typename H, typename ... R> struct tuple_element< n, tuple< H, R ...> > { typedef typename tuple_element< n-1, tuple< R ...> >::type type;};

template< typename H, typename ... R> struct tuple_element< 0, tuple< H, R ...> > { typedef H type;};

C++11: <tuple>

Teoretický pohled na šablonyTriky s typovým konstrukcemi

Zlomková aritmetika

template< intmax_t n, intmax_t d = 1> struct ratio;

Příklad použití

typedef ratio_add<ratio<9>, ratio<3,4>> platform;

static_assert( ratio_equal< platform, ratio< 975, 100>>::value);

Vyhodnocuje překladač• Včetně výpočtu největšího společného dělitele!

C++11: <ratio>

Teoretický pohled na šablonyVýpočty při kompilaci

Data: celá čísla typy

Funkcionální programování: Funkce bez vedlejších efektů Neexistuje přiřazovací příkaz

• "Proměnné" se nemění Rekurze Odlišného chování funkcí pro

různé hodnoty parametrů se dociluje definováním několika těl funkcí (tj. šablon)

Výpočty za běhu Data:

celá i reálná čísla, struktury ukazatelé

Procedurální programování: Procedury s vedlejšími efekty Destruktivní přiřazení

• Proměnné se mění Podmínky, cykly, rekurze Odlišného chování procedur

pro různé hodnoty parametrů se dociluje podmínkami uvnitř

LambdaLambda

Lambda výrazyMotivace

class ftor {public: ftor(int a, int b) : a_(a),b_(b) { } bool operator()(int x) const { return x*a_<b_; }private: int a_, b_; };

typedef std::vector<int> v_t; v_t v;

v_t::iterator vi=remove_if(v.begin(), v.end(), ftor(m, n));

Řešenístd::vector<int> v;

auto vi=remove_if(v.begin(), v.end(), [=](int x){ return x*m<n; });

C++11

Lambda výrazyLambda výraz

[ capture ]( params ) mutable -> rettype { body }

Deklaruje třídu ve tvaruclass ftor {public: ftor( TList ... plist) : vlist( plist) ... { } rettype operator()( params ) const { body }private: TList ... vlist;};

vlist je určen proměnnými použitými v body TList je určen jejich typy a upraven podle capture operator() je const pokud není uvedeno mutable

Lambda výraz je nahrazen vytvořením objektuftor( vlist ...)

C++11

Lambda výrazy – návratový typ a typ funkceNávratový typ operátoru

Explicitně definovaný návratový typ[]() -> int { … }

Automaticky určen pro tělo lambda funkce ve tvaru[]() { return V; }

Jinak void

C++11

Lambda výrazy – captureCapture

[ capture ]( params ) mutable -> rettype { body } 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ší entity v capture

[a,&b,c,&d]• entity označené & předány odkazem, ostatní hodnotou

Implicitní capture Překladač sám určí vnější entity, capture určuje způsob předání

[=][=,&b,&d]

• předání hodnotou, vyjmenované výjimky odkazem[&][&,a,c]

• předání odkazem, vyjmenované výjimky hodnotou

C++11

Lambda výrazy – příkladint a = 1, b = 1, c = 1;auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std::cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2();};a = 2; b = 2; c = 2;m1();std::cout << a << b << c;

Co to vypíše?

123234

C++11

Exception handlingException handlingMechanismus výjimek

Exception handlingMechanismus výjimek

Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

class AnyException { /*...*/ };class WrongException : public AnyException { /*...*/ };class BadException : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException( something); if ( anything != good ) throw BadException( anything);}void g(){ try { f(); } catch ( const AnyException & e1 ) { /*...*/ }}

Exception handlingMechanismus výjimek

Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

Hodnotu není třeba využívat

class AnyException { /*...*/ };class WrongException : public AnyException { /*...*/ };class BadException : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException();}void g(){ try { f(); } catch ( const AnyException &) { /*...*/ }}

Exception handlingMechanismus výjimek

Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

Hodnotu není třeba využívat Existuje univerzální catch blok

class AnyException { /*...*/ };class WrongException : public AnyException { /*...*/ };class BadException : public AnyException { /*...*/ };void f(){ if ( something == wrong ) throw WrongException(); if ( anything != good ) throw BadException();}void g(){ try { f(); } catch (...) { /*...*/ }}

Exception handlingFáze zpracování výjimky

Vyhodnocení výrazu v příkaze throw Hodnota je uložena "stranou"

Stack-unwinding Postupně se opouštějí bloky a funkce, ve kterých bylo provádění

vnořeno Na zanikající lokální a pomocné proměnné jsou volány destruktory Stack-unwinding končí dosažením try-bloku, za kterým je catch-blok

odpovídající typu výrazu v příkaze throwProvedení kódu v catch-bloku

Původní hodnota throw je stále uložena pro případné pokračování:• Příkaz throw bez výrazu pokračuje ve zpracování téže výjimky počínaje

dalším catch-blokem - začíná znovu stack-unwindingZpracování definitivně končí opuštěním catch-bloku

Běžným způsobem nebo příkazy return, break, continue, goto• Nebo vyvoláním jiné výjimky

Exception handlingZhmotněné výjimky

std::exception_ptr je chytrý ukazatel na objekt výjimky

Objekt zanikne při zániku posledního ukazatele

std::current_exception() Vrací aktuálně řešenou výjimku

std::rethrow_exception( p) Vyvolává uloženou výjimku

Tento mechanismus umožňuje odložit ošetřování výjimky, zejména:

Propagace výjimky do jiného vlákna

Řešení výjimek v promise/future

std::exception_ptr p;

void g(){ try { f(); } catch (...) { p = std::current_exception(); }}

void h(){ std::rethrow_exception( p);}

C++11

Exception handlingPoužití mechanismu výjimek

Vyvolání a zpracování výjimky je relativně časově náročné Používat pouze pro chybové nebo řídké stavy

• Např. nedostatek paměti, ztráta spojení, chybný vstup, konec souboru

Připravenost na výjimky také něco (málo) stojí Za normálního běhu je třeba zařídit, aby výjimka dokázala najít cíl a

zrušit proměnné• Výjimky se týkají i procedur, ve kterých není ani throw, ani try-blok

Většina kompilátorů umí překládat ve dvou režimech "s" a "bez"• Celý spojovaný program musí být přeložen stejně

Exception handlingStandardní výjimky

<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášenímbad_alloc: vyvolává operátor new při nedostatku paměti

V režimu "bez výjimek" new vrací nulový ukazatelbad_cast, bad_typeid: Chybné použití RTTIOdvozené z třídy logic_error:

domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[]

Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error

Exception handlingStandardní výjimky

<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášenímbad_alloc: vyvolává operátor new při nedostatku paměti

V režimu "bez výjimek" new vrací nulový ukazatelbad_cast, bad_typeid: Chybné použití RTTIOdvozené z třídy logic_error:

domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[]

Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error

Aritmetické ani ukazatelové operátory na vestavěných typech NEHLÁSÍ běhové chyby prostřednictvím výjimek

např. dělení nulou nebo dereference nulového ukazatele

Exception specificationsException specifications

U každé funkce (operátoru, metody) je možno určit seznam výjimek, kterými smí být ukončena

Na výjimky ošetřené uvnitř funkce se specifikace nevztahuje

Pokud není specifikace uvedena, povoleny jsou všechny výjimky

Specifikace respektuje dědičnost, to jest automaticky povoluje i všechny potomky uvedené třídy

void a(){ /* tahle smí všechno */}

void b() throw (){ /* tahle nesmí nic */}

void c() throw ( std::bad_alloc){ /* tahle smí std::bad_alloc */}

void d() throw ( std::exception, MyExc){ /* tahle smí potomky std::exception a MyExc */}

Exception specificationsException specifications

Kompilátor zajistí, že nepovolená výjimka neopustí funkci: Pokud by se tak mělo stát, volá se unexpected()

• unexpected() smí vyvolat "náhradní" výjimku Pokud ani náhradní výjimka není povolena, zkusí se vyvolat

std::bad_exception Pokud ani std::bad_exception není povoleno, volá se terminate() a

program končí

Exception specificationsException specifications

Kompilátor zajistí, že nepovolená výjimka neopustí funkci

Toto je běhová kontrola Kompilátor smí vydávat

nejvýše varování Funkce smí volat jinou, která

by mohla vyvolat nepovolenou výjimku (ale nemusí)

void f() throw ( std::exception){}

void g() throw (){ f(); /* tohle se smí */}

Exception specificationsException specifications

throw( T) specifikace se příliš nepoužívaly

C++11 definuje novou syntaxi noexcept noexcept( c) kde c je

Booleovský konstantní výraz

void f() noexcept{}

template< typename T>void g( T & y) noexcept( std::is_nothrow_copy_constructible < T>::value){ T x = y;}

C++11

Exception-safe programmingException-safe programmingBezpečné programování s výjimkami

Exception-safe programming

Používat throw a catch je jednoduché

Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek

Exception-safety Exception-safe programming

void f(){ int * a = new int[ 100]; int * b = new int[ 200]; g( a, b); delete[] b; delete[] a;}

Pokud new int[ 200] způsobí výjimku, procedura zanechá naalokovaný nedostupný blok

Pokud výjimku vyvolá procedura g, zůstanou dva nedostupné bloky

Exception-safe programming

Používat throw a catch je jednoduché

Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek

Exception-safety Exception-safe programming

T & operator=( const T & b){ if ( this != & b ) { delete body_; body_ = new TBody( b.length()); copy( body_, b.body_); } return * this;}

Pokud new TBody způsobí výjimku, operátor= zanechá v položce body_ původní ukazatel, který již míří na dealokovaný blok

Pokud výjimku vyvolá procedura copy, operátor zanechá třídu v neúplném stavu

Exception-safe programmingPravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena

nejpozději uvnitř destruktoru

Zdůvodnění: V rámci ošetření výjimek (ve fázi stack-unwinding) se volají

destruktory lokálních proměnných Výjimku zde vyvolanou nelze z technických i logických důvodů

ošetřit (ztratila by se původní výjimka) Nastane-li taková výjimka, volá se funkce terminate() a program

končí

Exception-safe programmingPravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena

nejpozději uvnitř destruktoru

Toto pravidlo jazyka sice platí pouze pro destruktory lokálních proměnných

A z jiných důvodů též pro globální proměnnéJe však vhodné je dodržovat vždy

Bezpečnostní zdůvodnění: Destruktory lokálních proměnných často volají jiné destruktory

Logické zdůvodnění: Nesmrtelné objekty nechceme

Exception-safe programmingPravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Zdůvodnění: Není místo, kde ji zachytit Stane-li se to, volá se terminate() a program končí Jiné konstruktory ale výjimky volat mohou (a bývá to vhodné)

Exception-safe programmingPravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky

Zdůvodnění: Catch blok by nebylo možné vyvolat Stane-li se to, volá se terminate() a program končí Jiné copy-constructory ale výjimky volat mohou (a bývá to vhodné)

Exception-safe programmingPravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky

Exception-safe programmingPoznámka: Výjimky při zpracování výjimky

Výjimka při výpočtu výrazu v throw příkaze Tento throw příkaz nebude vyvolán

Výjimka v destruktoru při stack-unwinding Povolena, pokud neopustí destruktor Po zachycení a normálním ukončení destruktoru se pokračuje v

původní výjimce

Výjimka uvnitř catch-bloku Pokud je zachycena uvnitř, ošetření původní výjimky může dále

pokračovat (přikazem throw bez výrazu) Pokud není zachycena, namísto původní výjimky se pokračuje

ošetřováním nové

Exception-safe programmingKompilátory samy ošetřují některé výjimky

Dynamická alokace polí Dojde-li k výjimce v konstruktoru některého prvku, úspěšně

zkonstruované prvky budou destruovány• Ve zpracování výjimky se poté pokračuje

Exception-safe programmingKompilátory samy ošetřují některé výjimky

Dynamická alokace polí Dojde-li k výjimce v konstruktoru některého prvku, úspěšně

zkonstruované prvky budou destruovány• Ve zpracování výjimky se poté pokračuje

Výjimka v konstruktoru součásti (prvku nebo předka) třídy Sousední, již zkonstruované součásti, budou destruovány Ve zpracování výjimky se poté pokračuje

• Uvnitř konstruktoru je možno výjimku zachytit speciálním try-blokem:X::X( /* formální parametry */)try : Y( /* parametry pro konstruktor součásti Y */) { /* vlastní tělo konstruktoru */} catch ( /* parametr catch-bloku */ ) { /* ošetření výjimky v konstruktoru Y i ve vlastním těle */}

Konstrukci objektu nelze dokončit• Opuštění speciálního catch bloku znamená throw;

Exception-safe programmingDefinice

(Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v

případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména:

• Nedostupná data byla korektně destruována a odalokována• Ukazatele nemíří na odalokovaná data• Platí další invarianty dané logikou aplikace

Exception-safe programmingDefinice

(Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v

případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména:

• Nedostupná data byla korektně destruována a odalokována• Ukazatele nemíří na odalokovaná data• Platí další invarianty dané logikou aplikace

Strong exception safety Funkce je silně bezpečná, pokud v případě, že skončí vyvoláním

výjimky, zanechá data ve stejném (pozorovatelném) stavu, ve kterém byla při jejím vyvolání

• Observable state - chování veřejných metod Nazýváno též "Commit-or-rollback semantics"

Exception-safe programmingPoznámky

(Weak) exception safety Tohoto stupně bezpečnosti lze většinou dosáhnout Stačí vhodně definovat nějaký konzistentní stav, kterého lze vždy

dosáhnout, a ošetřit pomocí něj všechny výjimky• Konzistentním stavem může být třeba nulovost všech položek• Je nutné upravit všechny funkce tak, aby je tento konzistentní stav

nepřekvapil (mohou na něj ale reagovat výjimkou)

Strong exception safety Silné bezpečnosti nemusí jít vůbec dosáhnout, pokud je rozhraní

funkce navrženo špatně Obvykle jsou problémy s funkcemi s dvojím efektem

• Příklad: funkce pop vracející odebranou hodnotu

Exception-safe programmingException-safe programmingKonstruktory a operator=

Exception-safe programmingcopy-constructor

Silně bezpečné řešení Pokud tělo dorazí na konec,

budou datové položky korektně vyplněny

Tělo může vyvolávat výjimky• V takovém případě není třeba

datové položky vyplňovat• Objekt nebude považován za

platný a nebude používán ani destruován

Obecně je však třeba ošetřit try-blokem situace, kdy je v objektu více dynamicky alokovaných ukazatelů

• Vyplatí se uzavírat ukazatele do tříd po jednom

• Chytré ukazatele

class String { /*...*/ char * str_;};

String( const String & b){ if ( b.str_ ) { str_ = new char[ strlen( b.str_) + 1]; strcpy( str_, b.str_); } else { throw InvalidString(); }}

Exception-safe programmingcopy-constructor

Silně bezpečné řešení

Chytré ukazatele std::unique_ptr< T>

#include <memory>

class String { /*...*/ std::unique_ptr< char[]> str_;};

String( const String & b){ if ( b.str_ ) { str_.reset( new char[ strlen( b.str_) + 1]); strcpy( str_.get(), b.str_.get()); } else { throw InvalidString(); }}

C++11

Exception-safe programmingoperator=

Silně bezpečné řešení Pokud je copy-constructor silně

bezpečný Copy-constructor naplní lokální

proměnnou c kopií parametru b• Zde může dojít k výjimce

Metoda swap_with vyměňuje obsah this a proměnné c

• Knihovní funkce swap je rychlá a (na ukazatelích) nevyvolává výjimky

Před návratem z operatoru se volá destruktor c

• Tím zaniká původní obsah this

#include <algorithm>

void String::swap_with( String & x){ swap( str_, x.str_);}

String & String::operator=( const String & b){ String c( b); swap_with( c); return * this;}

Exception-safe programmingoperator=

Silně bezpečné řešení

Metodu swap_with je vhodné publikovat ve formě globální funkce se standardním jménem swap

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

#include <algorithm>

void String::swap_with( String & x){ swap( str_, x.str_);}

String & String::operator=( const String & b){ String c( b); swap_with( c); return * this;}

void swap( String & x, String & y){ x.swap_with( y);}

před C++11

Exception-safe programmingMove metody

Obvykle negenerují výjimky

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

Vlastní implementace globální funkce swap není zapotřebí

Knihovní implementace swap volá move metody

#include <algorithm>

String::String( String && b) : str_( std::move( b.str_)){}

String & String::operator=( String && b){ str_ = std::move( b.str_); return * this;}

C++11

Exception-safe programmingException-safe programmingFunkce s vedlejšími efekty

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementace: Při výjimce v konstruktoru

proměnné s se nestane nic operator delete nezpůsobuje

výjimky

struct Box { String v; Box * next; };

class StringStack {public: // ... private: Box * top_;};

String StringStack::pop(){ if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; delete p; return s;}

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

struct Box { String v; Box * next; };

class StringStack {public: // ... private: Box * top_;};

String StringStack::pop(){ if ( ! top_ ) throw StackEmpty(); Box * p = top_; String s = p->v; top_ = p->next; delete p; return s;}

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Silně bezpečná implementace Jak zrušíme proměnnou p,

když k výjimce nedojde? std::unique_ptr< T>

#include <memory>

String StringStack::pop(){ if ( ! top_ ) throw StackEmpty(); std::unique_ptr< Box> p = top_; top_ = p->next; try { return p->v; } catch ( ...) { top_ = std::move( p); // toto přiřazení nuluje p throw; }}// při návratu se automaticky zruší p// pokud je p nenulové

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Silně bezpečná implementace Uživatel ji nedokáže použít tak,

aby to bylo silně bezpečné Vracenou hodnotu je nutné

okopírovat Nedá se poznat, zda výjimku

vyvolala metoda pop nebo operator=

• V prvním případě je zásobník nedotčen, ale ve druhém je již zkrácen

StringStack stk;String a;

/* ... */

try { a = stk.pop();}catch (...){ /* ??? */}

Exception-safe programmingPoučení

Funkce, která má vedlejší efekty, smí vracet hodnotou pouze typy, jejichž kopírování nevyvolává výjimky

a = stk.pop();

Pokud je třeba nějakou "nebezpečnou" hodnotu vracet, musí se předávat jako výstupní parametr

stk.pop( a);

Funkcí bez vedlejších efektů se problém netýká

a = b + c;

StringStack stk;String a;

/* ... */

try { a = stk.pop();}catch (...){ /* ??? */}

Většiny operátorů se problém netýká: buď nemají vedlejší efekty, nebo vracejí trvale existující objekt odkazem

•Problematické jsou postfixové ++, --

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení A Jako v STL Rozdělit pop na dvě funkce

top vrací vrchol zásobníku• může jej vracet odkazem• nemodifikuje data

pop pouze zkracuje• je silně bezpečná

StringStack stk;String a;

/* ... */

try { a = stk.top();}catch (...){ /* chyba kopírování nebo prázdný zásobník, proměnná a nezměněna, zásobník nedotčen */}try { stk.pop();}catch (...){ /* chyba zkracování, proměnná a změněna, zásobník nedotčen */}

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení B Namísto vracení hodnoty

funkce pop vyplňuje parametr předávaný odkazem

tím se vyloučí nutnost kombinovat volání pop s dalším kopírováním

Pro uživatele jednodušší, implementace pop je však těžší

StringStack stk;String a;

/* ... */

try { stk.pop( a);}catch (...){ /* chyba zkracování nebo kopírování, proměnná a nezměněna, zásobník nedotčen */}

Exception-safe programmingPříklad: StringStack::pop

Zásobník prvků typu String Implementován seznamem

Řešení B Lze implementovat nad

řešením A

#include <memory>

class StringStack {public: /* A */ String & top(); void pop();

/* B */ void pop( String & out) { String & t = top(); swap( out, t); try { pop(); } catch (...) { swap( out, t); throw; } }};

Kompilační a běhovýKompilační a běhovýpolymorfismuspolymorfismus

Příklad

Příklad: Vektorové operace

template< typename P>std::vector< typename P::result_type> for_vector(

P g, std::vector< typename P::first_argument_type> x, const std::vector< typename P::second_argument_type> & y)

{transform(

make_move_iterator( begin( x)), make_move_iterator( end( x)), begin( y),begin( x), g);

return std::move( x);}

result_type, first_argument_type a second_argument_type jsou součástí binárních funktorů definovaných normou C++• zde jsou použity k určení typů vektorových operandů a především

výsledku make_move_iterator umožňuje vykrást původní elementy vektoru x

• operátor * vrací r-value

C++11

Příklad: Vektorové operace

typedef std::vector< int> my_vector;my_vector x( 3, 7), y( 3, 2);

auto op = std::plus< int>();

my_vector z = for_vector( op, x, y);

std::plus< int> je binární funktor definovaný normou C++• obaluje operátor + do funktoru, doplňuje typové položky

C++11

Polymorfismus: Dynamicky volené operace

std::string s = ...;

typedef std::vector< int> my_vector;my_vector x( 3, 7), y( 3, 2);

auto op = s == ”+” ? std::plus< int>() : std::minus< int>();

my_vector z = for_vector( op, x, y);

Chyba: std::plus< int> a std::minus< int> jsou rozdílné typy• Operátor ? : je nedokáže převést na společný typ

C++11

Polymorfismus: Dynamicky volené operace

std::string s = ...;

typedef std::vector< int> my_vector;my_vector x( 3, 7), y( 3, 2);

typedef std::function< int( int, int)> my_function;auto op = s == "+" ?

my_function( std::plus< int>()) : my_function( std::minus< int>());

my_vector z = for_vector( op, x, y);

Chyba odstraněna:• std::function< int( int, int)> je polymorfní obálka schopná pojmout

všechny funktory se signaturou int( int, int)• Tato obálka se opět chová jako funktor

Řešení je velmi neefektivní• Polymorfní funktor je vyvoláván pro každý prvek vektoru• Nepřímé volání funkce stojí výrazně více než samotné sečtení

C++11

Polymorfismus: Dynamicky volené operace

std::string s = ...;

typedef std::vector< int> my_vector;my_vector x( 3, 7), y( 3, 2);

auto opplus = vectorize( std::plus< int>());auto opminus = vectorize( std::minus< int>());

typedef std::function< my_vector( my_vector, const my_vector &)> my_vector_function;auto op = s == "+" ? my_vector_function( opplus) : my_vector_function( opminus);

z = op( x, y);

Polymorfismus se odehrává až na vektorizovaných operacích• Polymorfní obálka je funktor nad vektory• Nepřímé volání bude jen jedno

Zbývá napsat funkci vectorize• Transformuje skalární funktor na vektorový

C++11

Polymorfismus: Dynamicky volené operacetemplate< typename P>struct vectorized{

vectorized( P g) : g_( g) {}typedef std::vector< typename P::first_argument_type> first_argument_type;typedef std::vector< typename P::second_argument_type> second_argument_type;typedef std::vector< typename P::result_type> result_type;

result_type operator()( first_argument_type x, const second_argument_type & y) const{ return for_vector( g_, std::move( x), y); }

private:P g_;

};

template< typename P>vectorized< P> vectorize( P g){ return vectorized< P>( g); }

vectorized< P> je vektorová verze funktoru P

Funkce vectorize umožňuje automatické odvození typu P z parametru g

C++11

Polymorfismus: Dynamicky volené operaceauto opplus = vectorize( std::plus< int>());auto opminus = vectorize( std::minus< int>());

auto op = s == "+" ? dynamize( opplus) : dynamize( opminus);

Funkce dynamize umožňuje automatické odvození typu polymorfní obálky

template< typename P>std::function< typename P::result_type(

typename P::first_argument_type, typename P::second_argument_type)> dynamize( P g)

{return std::function< typename P::result_type(

typename P::first_argument_type, typename P::second_argument_type)> ( g);

}

Tento zápis je mimořádně neprůhledný

C++11

Polymorfismus: Dynamicky volené operace Srozumitelnější zápis

Trait dynamize_type umožňuje automatické odvození typu polymorfní obálky

template< typename P>struct dynamize_type{

typedef std::function< typename P::result_type( typename P::first_argument_type, typename P::second_argument_type)> type;

};

Nepřímé využití dynamize_type

template< typename P>typename dynamize_type< P>::type dynamize( P g){

return dynamize_type< P>::type( g);}

Přímé využití dynamize_type

typedef std::binary_function< my_vector, my_vector, my_vector> my_function;std::map< std::string, dynamize_type< my_function>::type> operator_map;

operator_map.emplace( “+”, vectorize( std::plus< int>()));

C++11

Koenig lookupKoenig lookup

iostreamProblém: namespace

namespace prostor { class Souradnice { public: int x, y; };

std::ostream & operator<<( std::ostream & s, const Souradnice & a) { return s << '[' << a.x << ',' << a.y << ']'; }};

prostor::Souradnice p;

std::cout << p; // správný operator<< je v namespace prostor,// který není přímo vidět

iostreamProblém: namespace

namespace prostor { class Souradnice { public: int x, y; };

std::ostream & operator<<( std::ostream & s, const Souradnice & a) { return s << '[' << a.x << ',' << a.y << ']'; }};

prostor::Souradnice p;

std::cout << p; // správný operator<< je v namespace prostor,// který není přímo vidět

std::cout << std::endl; // tentýž problém je ale už tady:// tento operator<< je v namespace std

Koenig lookupprostor::Souradnice p;std::cout << p; // správný operator<< je v namespace prostor,

// který není přímo vidětstd::cout << std::endl; // tentýž problém je ale už tady:

// tento operator<< je v namespace std

Oba případy jsou překládány správněJe k tomu nutná složitá definice vyhledávání identifikátoru

tzv. Koenigovo vyhledávání používá se, je-li význam identifikátoru závislý na parametrech

• volání funkce• použití operátoru

Koenig lookupKoenigovo vyhledávání (zjednodušeno)

Argument-dependent name lookup (ISO C++)Pro každý skutečný parametr se z jeho typu T určí množina

asociovaných namespace Je-li T číselný, tyto množiny jsou prázdné Je-li T union nebo enum, jeho asociovaným namespace je ten, ve

kterém je definován Je-li T ukazatel na U nebo pole U, přejímá asociované namespace

od typu U Je-li T funkce nebo ukazatel na funkci, přejímá (sjednocením)

asociované namespace všech parametrů a návratového typu Je-li T třída, asociovanými namespace jsou ty, v nichž jsou

definovány tato třída a všichni její přímí i nepřímí předkové Je-li T instancí šablony, přejímá kromě asociovaných tříd a

namespace definovaných pro třídu také asociované třídy a namespace všech typových argumentů šablony

Koenig lookupKoenigovo vyhledávání (zjednodušeno)

Argument-dependent name lookup (ISO C++)Pro každý skutečný parametr se z jeho typu T určí množina

asociovaných namespaceIdentifikátor funkce se pak vyhledává v těchto prostorech

Globální prostor a aktuální namespace Všechny namespace přidané direktivami using Sjednocení asociovaných namespace všech parametrů funkce

Všechny varianty funkce nalezené v těchto namespace jsou rovnocenné

Mezi nimi se vybírá podle počtu a typu parametrů• Pokud není jednoznačně určena nejlepší varianta, je to chyba

Volání v kontextu třídy: Je-li identifikátor nalezen uvnitř této třídy nebo některého předka (jako metoda), má přednost před výše uvedenými variantami (globálními funkcemi)

----------------------------------------------------------------------