Minule jsme si p°edstavili koncepty (metatypy) iterßtor∙. Dnes se budeme bavit p°evß₧n∞ o modelech t∞chto koncept∙, tedy z pohledu C++ o typech a jejich vlastnostech.
Iterßtorem m∙₧e b²t klasick² ukazatel a stejn∞ tak i t°φda, kterß implementuje po₧adovanΘ rozhrannφ (s po₧adovanou sΘmantikou). Prost°edky C++ vÜak neumo₧≥ujφ pracovat p°φmo s koncepty; nßzev konceptu lze pou₧φt jako jmΘno ÜablonovΘho parametru a tφm to konΦφ (JeÜt∞ lze s pomocφ jist²ch trik∙ testovat, zda dan² typ je modelem konceptu, ale pouze na syntaktickΘ ·rovni).
Pokud tedy chceme v∞d∞t, zda dan² iterßtor (typ) je vstupnφ, dop°edn² Φi s nßhodn²m p°φstupem musφme si je oznaΦkovat. Ka₧dΘmu iterßtoru p°i°adφme prßv∞ jednu znaΦku, kterß bude oznaΦovat ten nejlepÜφ koncept, jeho₧ je iterßtor modelem. ZnaΦky nebudou nic jinΘho, ne₧ prßzdnΘ C++ t°φdy. Jednomu konceptu odpovφdß jeden datov² typ.
V STL jsou tyto znaΦky definovßny v hlaviΦkovΘm souboru <iterator>. Objektovß hierarchie t∞chto znaΦek odpovφdß p°ibli₧n∞ hierarchii p°φsluÜn²ch koncept∙, viz Obr. 1. Vlastnφ definice t∞chto znaΦek vypadajφ takto (jeden p°φklad za vÜechny)
class forward_iterator_tag
áá : public input_iterator_tag
{};
Obr. 1 Objektovß hierarchie znaΦek iterßtor∙.
K Φemu je znaΦkovßnφ dobrΘ? StaΦφ si uv∞domit, ₧e n∞kterΘ algoritmy fungujφ efektivn∞ji, kdy₧ majφ k dispozici mocn∞jÜφ iterßtory. Nap°φklad minule zmi≥ovanΘ t°φd∞nφ by fungovalo i s dop°edn²mi iterßtory, ale teprve kdy₧ pou₧ijeme iterßtory s nßhodn²m p°φstupem dostaneme efektivnφ algoritmus. Kdy₧ tohle spojφme s p°et∞₧ovßnφm funkcφ, m∙₧eme dßt t°φdφcφ funkci na v²b∞r mezi obecn∞jÜφm iterßtorem a mßlo efektivnφm algoritmem nebo specializovan²m iterßtorem a efektivnφm algoritmem.
NejlΘpe bude uvΘst si konkrΘtnφ p°φklad. Tentokrßt p∙jde o obrßcenφ po°adφ prvk∙ v posloupnosti (prvnφ bude poslednφ atd.). Zde je mo₧nΘ pou₧φt obousm∞rnΘ iterßtory, ale iterßtory s nßhodn²m p°φstupem dßvajφ efektivn∞jÜφ algoritmus. Nejprve napφÜeme funkci pro obousm∞rnΘ iterßtory a pak pro iterßtory s nßhodn²m p°φstupem:
template
<typename BidirectionalIterator>
void
obrat_poradi(BidirectionalIterator prvni,
ááááá BidirectionalIterator posledni,
ááááá bidirectional_iterator_tag)
{
áá while (true)
áá {
ááááá if ( (prvni
== posledni) || (prvni == --posledni) )
áááááááá return;
ááááá else
áááááááá std::iter_swap(prvni++,
posledni);
áá }
}
template
<typename RandomAccessIterator>
void
obrat_poradi(RandomAccessIterator prvni,
ááááá RandomAccessIterator posledni,
ááááá random_access_iterator_tag)
{
áá while (prvni < posledni) std::iter_swap(prvni++, --posledni);
}
Poslednφ t°etφ parametr p°edstavuje znaΦku iterßtoru. Je nepojmenovan², proto₧e jeho instance se v t∞le funkce v∙bec nepou₧ije. Slou₧φ pouze p°ekladaΦi p°i hledßnφ vhodnΘ p°etφ₧enΘ funkce. Na dopln∞nou: STL funkce iter_swap, le₧φcφ v prostoru jmen std, provede prohozenφ hodnot, na n∞₧ odkazujφ jejφ dva parametry û iterßtory.
Po°ßd tomu jeÜt∞ n∞co chybφ û dispeΦer, tedy °φdφcφ funkce, kterß se postarß o zavolßnφ jednΘ z p°edchozφch funkcφ podle toho, jakΘ iterßtory dostane na vstupu. A co chybφ dispeΦerovi? Mo₧nost zeptat se libovolnΘho iterßtoru na jeho znaΦku. Pot°ebujeme tedy t°φdu rys∙ (template traits class), kterß bude implementovat mapovßnφ libovolnΘho typu iterßtoru na p°φsluÜnou znaΦku. STL pro tento ·Φel poskytuje Üablonovou t°φdu rys∙ iterator_traits.
èablonovß t°φda rys∙ iterßtor∙, iterator_traits, je definovßna v hlaviΦkovΘm souboru <iterator>. Poskytuje informace nejenom o znaΦce iterßtoru, ale i o typu prvku, typu rozdφlu iterßtor∙, typu ukazatele na prvek a typu reference na prvek. A to vÜechno pro libovoln² iterßtor, kter² je jejφm Üablonov²m argumentem. Definice tΘto t°φdy rys∙ je nßsledujφcφ (jednotlivΘ definice typ∙ jsou uvedeny v po°adφ, v jakΘm byly v²Üe vyjmenovßny)
template <typename Iterator>
struct iterator_traits
{
áá typedef typename
ááááá Iterator::iterator_category
iterator_category;
áá typedef typename
ááááá Iterator::value_type
value_type;
áá typedef typename
ááááá Iterator::difference_type
difference_type;
áá typedef typename
ááááá Iterator::pointer
pointer;
áá typedef typename
ááááá Iterator::reference
reference;
};
Struktura ka₧dΘ z on∞ch definicφ typu je nßsledujφcφ: KlφΦovΘ slovo typedef uvßdφ vno°enou definici typu, nßsleduje klφΦovΘ slovo typename, kterΘ p°ekladaΦi napovφ, ₧e nßsledujφcφ v²raz Iterator::iterator_category je oznaΦenφm vno°enΘho typu v typu Iterator. Jako poslednφ se objevφ jmΘno definovanΘho typu, tj. iterator_category.
Tato definice p°edpoklßdß, ₧e iterßtor (typ) je t°φda a mß vno°enΘ definice typ∙. My vÜak z minula vφme, ₧e klasick² ukazatel je rovn∞₧ iterßtor. Co s tφm? Ukazatel p°ece nem∙₧e mφt vno°enΘ definice typ∙. NaÜt∞stφ jsou tu pokroΦilΘ vlastnosti C++, konkrΘtn∞ ΦßsteΦnß specializace Üablony t°φdy. Nic nßm tedy nebrßnφ napsat specializaci pro klasickΘ ukazatele
template <typename T>
struct iterator_traits<T *>
{
áá typedef
ááááá random_access_iterator_tag
iterator_category;
áá typedef ptrdiff_tá
difference_type;
áá typedef Tá value_type;
áá typedef T *á
pointer;
áá typedef T &
reference;
};
template <typename T>
struct iterator_traits<const T *>
{ /* analogicky... */ };
Zde jasn∞ °φkßme, ₧e ukazatel je iterßtor s nßhodn²m p°φstupem. Rozdφl dvou ukazatel∙ je v C++ typu ptrdiff_t. Rozhodnutφ, zda pou₧φt nespecializovanou Φi specializovanou Üablonu, je v∞cφ p°ekladaΦe; o to se v∙bec nemusφme starat. Nynφ u₧ m∙₧eme doplnit dv∞ d°φve definovanΘ funkce o jejich dispeΦera
template
<typename BidirectionalIterator>
void
obrat_poradi(BidirectionalIterator prvni,
ááááá BidirectionalIterator posledni)
{
áá obrat_poradi(prvni, posledni,
ááááá iterator_traits<BidirectionalIterator>::iterator_category());
}
Nenechte se zmßst, ₧e se vÜechny funkce jmenujφ stejn∞ û o rozliÜenφ se postarß p°ekladaΦ a p°et∞₧ovßnφ funkcφ. Jak znßmo, tam kde funguje koncept, funguje i jeho zjemn∞nφ. Tak₧e tuto funkci m∙₧eme volat takΘ pro iterßtory s nßhodn²m p°φstupem, nebo¥ tento koncept zjem≥uje koncept obousm∞rnΘho iterßtoru (kter² je zde explicitn∞ vy₧adovßn). RozliÜenφ, o jak² koncept iterßtoru se jednß, je uskuteΦn∞no uvnit° funkce, p°i pou₧itφ t°φdy rys∙. Vy₧aduje to vÜak menÜφ vysv∞tlenφ. V²raz
iterator_traits<BidirectionalIterator>::iterator_category
p°edstavuje typ znaΦky iterßtoru. Nap°φklad
iterator_traits<int
*>::iterator_category
znamenß, jak jist∞ uhodnete, typ random_access_iterator_tag. Ty prßzdnΘ zßvorky za oznaΦenφm typu z toho ud∞lajφ (nepojmenovanou) instanci. Jin²mi slovy: jde o volßnφ implicitnφho konstruktoru.
Zdatn∞jÜφ programßto°i, kte°φ ovlßdajφ ÜablonovΘ metaprogramovßnφ (viz Φlßnek [6]), by to zvlßdli i bez tΘ nepojmenovanΘ instance. Nepou₧ilo by se p°et∞₧ovßnφ funkcφ, ale meta konstrukce, viz ukßzka v [10].
JeÜt∞ jeden zajφmav² p°φklad. Vzpome≥me na v²Üe zmφn∞nou funkci iter_swap. Jejφm ·kolem je prohodit dva prvky, na kterΘ odkazujφ jejφ dva parametry, iterßtory. K prohozenφ prvk∙ pot°ebujeme vytvo°it pomocnou instanci prvku, ale k tomu pot°ebuje znßt typ prvku. Nenφ nic jednoduÜÜφho, ne₧ se zeptat iterßtoru na jeho p°φsluÜn² typ prvku pomocφ t°φdy iterator_traits.
template <typename ForwardIterator1,
ááááááááá typename
ForwardIterator2>
inline void iter_swap(ForwardIterator1 a,
ááááááá ááForwardIterator2 b)
{
áá iterator_traits<ForwardIterator1>::value_type
tmp(*a);
áá *a = *b;
áá *b = tmp;ááá
}
Z minula vφme, ₧e pro v²stupnφ iterßtory nemß smysl definovat typ prvku (value_type) ani typ rozdφlu (difference_type). V STL se to vyjßd°φ tak, ₧e se pou₧ije typ void. Tak₧e t°φda iterator_traits bude mφt pro jak²koliv v²stupnφ iterßtor obsah ekvivalentnφ s
{
áá typedef output_iterator_tag iterator_category;
áá typedef void
difference_type;
áá typedef void
value_type;
áá typedef void
pointer;
áá typedef void
reference;
};
Kdy₧ budeme psßt vlastnφ iterßtor, bude to zcela jist∞ t°φda. Pro kompatibilitu s STL budeme muset zajistit dostupnost rys∙ naÜeho iterßtoru. Zde existuje n∞kolik mo₧nostφ, jak toho dosßhnout.
╪ekn∞me, ₧e jsme vytvo°ili jist² kontejner, t°eba jednosm∞rn² seznam, a zßrove≥ tvo°φme i iterßtor na tomto kontejneru. Je jist∞ jasnΘ, ₧e jednosm∞rn² seznam nem∙₧e nabφdnout vφce ne₧ dop°edn² iterßtor. Zde je kostra definice naÜeho iterßtoru
template <typename
T> class cJednosmerka
{
áá // ...
};
P°edpoklßdejme, ₧e vÜechny vlastnosti, kterΘ vy₧aduje koncept dop°ednΘho iterßtoru, jsou ji₧ sprßvn∞ implementovßny. Nynφ si ukß₧eme, jak z n∞j ud∞lat iterßtor kompatibilnφ s STL, tj. uvedeme do ₧ivota rysy tohoto iterßtoru.
Jednou z mo₧nostφ je pou₧itφ ΦßsteΦnΘ specializace Üablony t°φdy rys∙ iterator_traits, tak jako v p°φpad∞ ukazatel∙. Jen₧e zatφmco pro ukazatel to byla jedinß mo₧nost, zde je to spφÜe exces, vy₧adujφcφ navφc ty nejpokroΦilejÜφ vlastnosti (kterΘ bohu₧el n∞kterΘ p°ekladaΦe jeÜt∞ neznajφ) i znalosti C++.
template <typename T>
struct iterator_traits<cJednosmerka<T> >
{
áá typedef forward_iterator_tag
iterator_category;
áá typedef ptrdiff_t
ádifference_type;
áá typedef Tá value_type;
áá typedef T *á
pointer;
áá typedef T &
reference;
};
DalÜφ mo₧nostφ je äruΦnφ prßceô. Prost∞ do naÜφ t°φdy vepφÜeme pot°ebnΘ definice typ∙. JednoduchΘ a snad i p°ehlednΘ.
template <typename
T> class cJednosmerka
{
áá // ...
public:
áá typedef forward_iterator_tag
iterator_category;
áá typedef ptrdiff_tá
difference_type;
áá typedef Tá value_type;
áá typedef T *á
pointer;
áá typedef T &
reference;
};
Pokud si vzpomenete na Φlßnek [5], na to jak jsme z neadaptabilnφch funktor∙ d∞lali adaptabilnφ, vφte, ₧e jsme m∞li v STL k dispozici dv∞ bßzovΘ t°φdy, kterΘ sice samy o sob∞ nebyly funktory, ale poskytovaly pot°ebnΘ definice typ∙. Stejnß situace je i u iterßtor∙. Mßme k dispozici bßzovou t°φdu iterator, kterß sice sama o sob∞ nenφ iterßtorem, ale poskytuje pot°ebnΘ vno°enΘ definice typ∙. Najdeme ji v hlaviΦkovΘm souboru <iterator>. Nßzev je mo₧nß zavßd∞jφcφ, ale s tφm se musφme smφ°it. Definice vypadß takto
template <typename Category,
ááááááááá typename
T,
ááááááááá typename
Distance = ptrdiff_t,
ááááááááá typename
Pointer = T*,
ááááááááá typename
Reference = T&>
struct iterator
{
áá typedef Category
iterator_category;
áá typedef T value_type;
áá typedef Distance
difference_type;
áá typedef Pointer
pointer;
áá typedef Reference
reference;
};
S pou₧itφm tΘto t°φdy m∙₧eme aktivovat rysy naÜeho iterßtoru tφm, ₧e ho odvodφme jako potomka
template <typename
T> class cJednosmerka
áá : public iterator<forward_iterator_tag,
T>
{
áá // ...
};
VÜimn∞te si, ₧e jsme zadali pouze prvnφ dva ÜablonovΘ argumenty. Ostatnφ majφ svΘ implicitnφ hodnoty, kterΘ ve v∞tÜin∞ p°φpad∙ nenφ nutno m∞nit. Toto je preferovan² zp∙sob.
Jak ji₧ vφme, v²stupnφ iterßtor je pon∞kud zvlßÜtnφ û v∞tÜina jemu asociovan²ch typ∙ je void. Proto mohou mφt vÜechny v²stupnφ iterßtory jednoho p°edka
class
cJakykolivVystupniIterator
áá : public iterator<output_iterator_tag,
áááááááááááááááááááá void, void, void, void>
{
áá // ...
};
Toto zajistφ, ₧e t°φda rys∙ iterator_traits bude obsahovat ty sprßvnΘ ·daje (viz v²Üe).
Pro zßkladnφ prßci s iterßtory poskytuje STL dv∞ funkce: advance pro posun iterßtoru o dan² poΦet krok∙ (vp°ed nebo i vzad podle konceptu iterßtoru), distance pro zjiÜt∞nφ vzdßlenosti mezi dv∞ma iterßtory (jeden musφ b²t dosa₧iteln² z druhΘho).
template <typename InputIterator, typename Distance>
void advance(InputIterator& i, Distance n);
template<typename InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);
Tyto funkce jsou implementovßny podobn∞ jako naÜe ädispeΦerskßô funkce v²Üe. To znamenß, ₧e danΘ funkce v₧dy poskytujφ co nejefektivn∞jÜφ algoritmus pro danΘ typy parametr∙. Nap°φklad pro iterßtory s nßhodn²m p°φstupem ob∞ metody pracujφ v konstantnφm Φase, zatφmco pro ostatnφ iterßtory pracujφ v lineßrnφm Φase (·m∞rnΘm poΦtu krok∙). Tak₧e nap°φklad
cJednosmerka i;á //
+ n∞jakß inicializace
cJednosmerka i2;á //
+ n∞jakß inicializace
advance(i,
10);
int vzdalenost = distance(i, i2);
znamenß, ₧e se pro iterßtor i desetkrßt zavolß operßtor ++ a pak se jeÜt∞ provede jist² poΦet iteracφ s kopiφ iterßtoru i, dokud se nedosßhne i2 (jinak toti₧ vzdßlenost urΦit nejde). Kde₧to
int j[20];á //
+ inicializace
int *
j2 = j + 20;
advance(j,
10);
int do_konce
= distance(j, j2);
je ekvivalentnφ operacφm
j
+= 10;
int
do_konce = j2 û j;
To jen dφky tomu, ₧e ukazatel je iterßtor s nßhodn²m p°φstupem.
PouΦenφ: Nepodce≥ujte tyto funkce. P°i ka₧dΘm p°esunu iterßtoru o v∞tÜφ poΦet krok∙ ne₧ 1 pou₧ijte funkci advance. Pro zjiÜt∞nφ vzdßlenosti mezi dv∞ma (dosa₧iteln²mi) iterßtory v₧dy pou₧ijte funkci distance.
V∞tÜina STL algoritm∙ pracuje s jist²m rozsahem prvk∙ stylem äod zaΦßtku do konceô, tj. pou₧φvajφ v∞tÜinou operßtor ++ k p°echodu mezi prvky. N∞kdy vÜak m∙₧e b²t ₧ßdoucφ obrßtit sled na äod konce k zaΦßtkuô. Abychom nemuseli p°episovat k≤d algoritm∙, nabφzφ STL adaptΘr iterßtoru, kter² obrßtφ sled prochßzenφ: Jeho operßtor ++ bude vlastn∞ odpovφdat operßtoru --adaptovanΘho iterßtoru a obrßcen∞. Zde je deklarace (hlaviΦkov² soubor <iterator>)
template <typename Iterator>
class reverse_iterator
: public iterator
áááááááá <
ááááááááááá typename
iterator_traits<Iterator>::iterator_category,
ááááááááááá typename
iterator_traits<Iterator>::value_type,
ááááááááááá typename iterator_traits<Iterator>::difference_type,
ááááááááááá typename
iterator_traits<Iterator>::pointer,
ááááááááááá typename
iterator_traits<Iterator>::reference
áááááááá >
{
áá // ...
áá typedef Iterator
iterator_type;
áá explicit reverse_iterator(Iterator
x);
áá Iterator base()
const;
áá // ...
};
Je jasnΘ, ₧e tento adaptΘr nelze pou₧φt na jednosm∞rn² iterßtor. Tak₧e Üablonov²m argumentem m∙₧e b²t obousm∞rn² iterßtor nebo iterßtor s nßhodn²m p°φstupem. Pokud nahlΘdnete do hlaviΦkovΘho souboru s definicφ tohoto adaptΘru, zjistφte, ₧e mß vÜechny metody odpovφdajφcφ konceptu iterßtoru s nßhodn²m p°φstupem; tedy nap°φklad
reference operator[](difference_type n) const;
pro p°φstup k libovolnΘmu prvku v konstantnφm Φase. Ale co kdy₧ adaptujeme pouze obousm∞rn² iterßtor? Ten p°ece takovou operaci neznß! Je to v po°ßdku? Ano je. Dφky pokroΦil²m vlastnostem C++, konkrΘtn∞ implicitnφmu vytvß°enφ instancφ Üablon: Pokud metodu pou₧iji, vygeneruje se automaticky pot°ebnß instance, ale pokud takovou metodu nepou₧iji, pak jako by nebyla (nevytvo°φ se instance). To znamenß, ₧e bude vÜechno v po°ßdku, dokud se nepokusφm pou₧φt n∞co, co pro obousm∞rnΘ iterßtory nefunguje. Tak₧e pokud adaptujeme obousm∞rnΘ iterßtory, je adaptΘr modelem obousm∞rnΘho iterßtoru. A pokud adaptujeme iterßtor s nßhodn²m p°φstupem je v²sledn² adaptΘr modelem iterßtoru s nßhodn²m p°φstupem.
JeÜt∞ je zde ale menÜφ problΘm s urΦenφm rozsahu pro tento zp∞tn² iterßtor. Dejme tomu, ₧e chceme projφt cel² kontejner. ZaΦφnßme na poslednφm prvku, kter² do rozsahu pat°φ, a tφm pßdem by druh² konec rozsahu m∞l b²t omezen prvnφm neexistujφcφm prvkem p°ed prvnφm prvkem kontejneru. Nic takovΘho vÜak norma nedovoluje (za kontejnerem ano, ale p°ed ne). Proto je pou₧ita finta. Rozsah zaΦφnß na prvnφm prvku za poslednφm a konΦφ na prvnφm prvku, ale zp∞tn² iterßtor odkazuje äo jeden prvekô, viz Φervenß Üipka na Obr. 2. P°esn∞ji by se to nechalo zapsat takto
&*(reverse_iterator(i)) == &*(i - 1)
Ka₧d² kontejner podporujφcφ obousm∞rnΘ iterßtory poskytuje vno°enΘ definice typ∙ reverse_iterator a const_reverse_iterator, a takΘ metody rbegin() a rend(), kterΘ vracejφ iterßtory t∞chto typ∙, viz Obr. 2.
Obr. 2 Zp∞tnΘ iterßtory rend(), rbegin() a j. Plnß Üipka oznaΦuje element, kter² zφskßme pou₧itφm operßtoru dereferencovßnφ, iterßtory rend() a end() nelze dereferencovat.
AdaptΘr reverse_iterator obsahuje n∞kolik novinek oproti iterßtor∙m. Tyto jsou vypsßny v deklaraci v ·vodu tΘto sekce. Vno°en² typ iterator_type umo₧≥uje zeptat se na typ adaptovanΘho iterßtoru. Explicitnφ jednoparametrick² konstruktor z iterßtoru ud∞lß zp∞tn² iterßtor, viz j = reverse_iterator(i) na Obr.2. Metoda base() vracφ iterßtor p∙vodnφho, adaptovanΘho typu, kter² odpovφdß aktußlnφ pozici zp∞tnΘho iterßtoru. Toho lze vyu₧φt p°i prßci s kontejnery nap°φklad v nßsledujφcφ situaci: S kontejnerem pracujeme pomocφ zp∞tn²ch iterßtor∙, hledßme t°eba v²skyt jistΘho prvku od konce kontejneru, a pak se rozhodneme tento prvek z kontejneru odstranit. Metoda kontejneru erase vÜak nevezme zp∞tn² iterßtor jako parametr, proto pomocφ metody base() zp∞tnΘho iterßtoru provedeme konverzi a pak u₧ erase bude fungovat.
P°φklady u₧itφ zp∞tn²ch iterßtor∙ najdete v [10]. Zde jen krßtkß ukßzka toho jak vypsat pozpßtku obsah pole na obrazovku v textovΘm re₧imu (ka₧d² prvek na vlastnφ °ßdek)
int pole[5]
= { 1, 2, 3, 4, 5 };
copy(reverse_iterator<int
*>(pole + 5), // rbegin
áááá reverse_iterator<int *>(pole),áááá // rend
áááá ostream_iterator(cout, ô\nô));
Mimochodem, vφte jak co nejjednoduÜeji s pou₧itφm STL napsat funkci, kterß zjistφ, zda danΘ slovo (°et∞zec) je palindrom? P°ipome≥me, ₧e palindrom je takovΘ slovo, kterΘ se i pozpßtku Φte stejn∞, nap°. oko, krk, level, atd. Deklarace bude vypadat takto
bool je_palindrom(const string & slovo);
Zkuste na to p°ijφt (Pro jednoduchost p°edpoklßdejte, ₧e slovo je skuteΦn∞ jedno slovo a neobsahuje mezery.). ╪eÜenφ se dozvφte v p°φÜtφm dφle.
Dnes se jeÜt∞ nedostalo na vklßdacφ iterßtory a iterßtory na vstupn∞/v²stupnφch proudech. Budeme se jim v∞novat p°φÜt∞.
Jaroslav Fran∞k
Infotipy
[1] Dokumentace STL od SGI: www.sgi.com\technology\stl
[2] J. Fran∞k, Iteratory I, Chip 01/03
[3] J. Fran∞k, Iteratory II, Chip 01/03
[4] J. Fran∞k, Nebojte se STL, Chip 12/01
[5] J. Fran∞k, Jak se na funktor volß II, Chip 02/02
[6] J. Fran∞k, D°inu nechte p°ekladaΦi!, Chip 01/01
[7] D. Musser et al., STL Tutorial and Reference Guide, 2001
[8] N. Josuttis, The C++ Standard Library, 1999
[9] S. Meyers, Effective STL, 2001
[10] zdrojov² k≤d v rubrice Chip Plus na Chip CD