home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 February / Chip_2002-02_cd1.bin / obsahy / Chip_txt / txt / 136-141.txt < prev    next >
Text File  |  2002-01-06  |  20KB  |  263 lines

  1. Knihovna STL jazyka C++
  2. Jak se na funktor volß...  (1)
  3. D∙le₧itou souΦßstφ knihovny STL jazyka C++ jsou tzv. funktory, mezi programßtory vÜak o nich mnoho znßmo nenφ. V tomto dvoudφlnΘm p°φsp∞vku se o nich dozvφte vφce; mimo jinΘ takΘ poznßte, ₧e jde o starΘ znßmΘ v∞ci p°evleΦenΘ do novΘho kabßtu.
  4.  
  5. Funkce a funktor
  6. Volßnφ funkce je neodmysliteln∞ spjato s kulat²mi zßvorkami (), do kter²ch vepisujeme argumenty funkce. P°itom identifikßtor nebo v²raz p°edstavujφcφ funkci stojφ nalevo od t∞chto kulat²ch zßvorek. Otßzka znφ: Co vÜechno m∙₧e nalevo od kulat²ch zßvorek p°edstavujφcφch volßnφ funkce vlastn∞ stßt? Asi nejstruΦn∞jÜφ a nejv²sti₧n∞jÜφ odpov∞∩ dß obrßzek 1.
  7. Klasickß funkce
  8. Klasickß funkce je to, co znßme ze zßkladnφho kurzu jazyka C++ (nebo i cΘΦka). N∞kdy se jim takΘ °φkß (klasickΘ) cΘΦkovskΘ funkce, ale to nenφ p°φliÜ vhodnΘ, proto₧e funkce v C++ m∙₧e mφt t°eba definovßny implicitnφ hodnoty argument∙, a to (zatφm) v cΘΦku mo₧nΘ nenφ. Zkusme si uvΘst jednoduch² p°φklad - druhou mocninu celΘho Φφsla:
  9. int druha_mocnina(int cislo) 
  10. { return cislo * cislo; }
  11. S klasick²mi funkcemi pracujeme bu∩ p°φmo, tj. pou₧ijeme identifikßtor funkce, nebo pomocφ ukazatel∙ Φi referencφ. 
  12. Operßtor
  13. Specißlnφm p°φpadem klasick²ch funkcφ jsou operßtorovΘ funkce neboli krßtce operßtory. Jsou natolik specißlnφ, ₧e si zaslou₧φ vlastnφ kategorii. Zde je v²Φet jejich zvlßÜtnostφ:
  14. * Umo₧≥ujφ zkrßcen² zßpis volßnφ bez kulat²ch zßvorek.
  15. * P°i zkrßcenΘm zßpisu volßnφ mohou argumenty stßt nalevo (postfixov² operßtor), napravo (prefixov² operßtor), z obou stran (infixov² operßtor) nebo i jinak.
  16. * Operßtory pro vestav∞nΘ typy jsou p°φmo souΦßstφ p°ekladaΦe; nelze tedy zφskat jejich adresu (ukazatel) Φi referenci a nelze je volat pomocφ kulat²ch zßvorek (vφce viz standard [1], sekce 13.6.).
  17. VÜe objasnφ n∞kolik p°φklad∙.
  18. int operator +(int, int);
  19. Toto je jeden z vestav∞n²ch binßrnφch operßtor∙, konkrΘtn∞ souΦet dvou cel²ch Φφsel se znamΘnkem. Jeho typickΘ pou₧itφ vypadß nap°. takto:
  20. int a = 1, b = 2, c;
  21. c = a + b;
  22. Jazyk C++ umo₧≥uje definovat si vlastnφ verze operßtor∙ pro v²ΦtovΘ nebo objektovΘ typy. Tak₧e pokud mßme jednoduchou t°φdu sInt (definici naleznete v souboru f01_klasika.cpp na Chip CD 1/02) a definovali jsme (p°etφ₧en²) operßtor +
  23. sInt operator +(const sInt & a,
  24.                 const sInt & b);
  25. m∙₧eme psßt
  26. sInt a(1), b(2), c;
  27. c = a + b;
  28. TakΘ bychom mohli napsat
  29. c = operator +(a, b);
  30. ale to se pou₧φvß mßlokdy. Pak by toti₧ operßtory byly k niΦemu a vystaΦili bychom s klasick²mi funkcemi. Stejn∞ jako u klasick²ch funkcφ, pokud se rozhodneme pro volßnφ pomocφ kulat²ch zßvorek, m∙₧eme s operßtory pracovat p°φmo, nebo pomocφ ukazatel∙ Φi referencφ.
  31. ╚lenskß funkce 
  32. V objektov∞ orientovanΘm programovßnφ narazφme na dalÜφ typ funkce - tou je Φlenskß funkce (member function) nebo takΘ metoda objektu. (Pojem metoda t°φdy je vyhrazen pro statickou metodu objektu, kterß je v podstat∞ ekvivalentnφ s klasickou funkcφ.) ╚lenskß funkce je souΦßstφ definice t°φdy. Krßtk² p°φklad osv∞tlφ definici i pou₧itφ ΦlenskΘ funkce:
  33. class cNeco
  34. {
  35. public:
  36.    int clenska_funkce();
  37. private:
  38.    int soukroma_data;
  39. };
  40.  
  41. int cNeco::clenska_funkce()
  42.    { return soukroma_data; }
  43.  
  44. int main() {
  45.    sNeco a;
  46.    cout << a.clenska_funkce();
  47. }
  48. S Φlensk²mi funkcemi pracujeme bu∩ p°φmo (pou₧ijeme identifikßtor funkce), nebo prost°ednictvφm ukazatel∙ do t°φdy. Zde je ale situace slo₧it∞jÜφ ne₧ u obyΦejn²ch ukazatel∙ na klasickΘ funkce, proto₧e k tomu pot°ebujeme navφc p°φsluÜnou instanci. Uva₧ujme t°φdu cNeco z p°edchozφho p°φkladu:
  49. int main() {
  50.    int (cNeco::* pf)() =
  51.             &cNeco::clenska_funkce;
  52.    sNeco a;
  53.    cout << (a.*pf)();
  54.    cout << ((&a)->*pf)();
  55. }
  56. Samotn² ukazatel nestaΦφ, musφme pou₧φt n∞jakou instanci a jeden z operßtor∙ .* nebo ->* (podle situace). Teprve pak lze pou₧φt volßnφ funkce (to mß vyÜÜφ prioritu ne₧ oba operßtory, proto je musφme uzav°φt do zßvorek). Je tu jedna kuriozita: V C++ mß v²raz v₧dy n∞jak² typ, p°esn∞ji °eΦeno skoro v₧dy - krom∞ v²raz∙ se zmφn∞n²mi dv∞ma operßtory. V²raz a.*pf nebo (&a)->*pf je ve skuteΦnosti beztypovß (!) entita a jedinΘ, co se s nφ dß d∞lat, je bezprost°ednφ volßnφ funkce pomocφ kulat²ch zßvorek.
  57. Pro ·plnost: ╚lenskΘ metody lze jeÜt∞ dßle rozd∞lit na obyΦejnΘ, operßtory, metody t°φdy, specißlnφ (konstruktor, destruktor) atd.; to pro nßs vÜak nynφ nenφ podstatnΘ. PodstatnΘ ale je, ₧e mezi Φlensk²mi operßtory je i operßtor volßnφ funkce (). A kdy₧ pro t°φdu p°etφ₧φme tento operßtor, dostaneme funktor.
  58. Funktor
  59. Funktor je tedy obecn∞ ka₧dß t°φda, kterß mß ve°ejn∞ p°φstupn² p°etφ₧en² operßtor volßnφ funkce. Uve∩me si jako p°φklad analogii ·vodnφ klasickΘ funkce:
  60. class cDruhaMocnina
  61. {
  62. public:
  63.    int operator()(int cislo) 
  64.       { return cislo * cislo; }
  65. };
  66. Pou₧itφ je nasnad∞:
  67. int a = 1, b;
  68. cDruhaMocnina sqr;  // instance
  69. b = sqr(a);   // pou₧itφ
  70. Vidφme tedy, ₧e pou₧itφ funktoru se tΘm∞° v∙bec neliÜφ od pou₧itφ klasickΘ funkce - a o to tu jde. Dßle je jasnΘ, ₧e funktor lze takto pou₧φt pouze p°φmo nebo p°es referenci. Ukazatel na funktor je obyΦejn² ukazatel na n∞jakou instanci, tak₧e bychom ho museli bu∩ dereferencovat, nebo pou₧φt nezkrßcenΘ volßnφ operßtoru:
  71. cDruhaMocnina * psqr = &sqr;
  72. // dereferencovßnφ
  73. b = (*psqr)(a); 
  74. // nezkrßcen² zßpis 
  75. b = psqr->operator()(a);
  76. Co₧ je skoro jako p∞st na oko ve srovnßnφ s elegantnφm zßpisem sqr(a).
  77. Jeliko₧ nßm p∙jde p°edevÜφm o srovnßnφ funktor∙ s ostatnφmi funkcemi, pro "nefunktor" zavedeme pojem obyΦejnß funkce. ObyΦejnou funkcφ pro nßs tedy bude jak klasickß funkce a operßtor, tak i Φlenskß funkce.
  78.  
  79. Trocha motivace
  80. K Φemu jsou funktory vlastn∞ dobrΘ? JakΘ majφ v²hody oproti obyΦejn²m funkcφm? Vyplatφ se je v∙bec pou₧φvat? Zkusme se nad t∞mito otßzkami zamyslet hloub∞ji.
  81. Funkce parametrem Üablony
  82. Zabrusme na chvilku do STL. Mßme n∞jakou posloupnost cel²ch Φφsel a chceme jejich druhΘ mocniny. ╚φsla jsou ulo₧ena v n∞jakΘm vhodnΘm kontejneru (je jedno, jestli je to vector, deque, list nebo klasickΘ pole), kter² se jmenuje cisla. B∞₧n² programßtor by napsal asi toto:
  83. for ( int i = 0; i != N; ++i ) 
  84.       // N je velikost kontejneru
  85. {
  86.    cisla[i] *= cisla[i];
  87. }
  88. My zkuÜen∞jÜφ bychom ale spφÜ pou₧ili (dop°ednΘ) iterßtory:
  89. for ( iterator i = cisla.begin();
  90.       i != cisla.end(); ++i )
  91. {
  92.    *i *= *i;  // pozor na hv∞zdiΦky
  93. }
  94. kde iterator je typ p°φsluÜnΘho iterßtoru na naÜem kontejneru (pokud jde o klasickΘ pole, nebude to nic jinΘho ne₧ ukazatel int *; pak ovÜem budou meze zadßny trochu jinak - viz zdrojov² k≤d na Chip CD 1/02). 
  95. Ale proΦ po°ßd psßt smyΦky, kdy₧ to u₧ n∞kdo ud∞lal za nßs? V STL nalezneme funkci transform, kterß ud∞lß p°esn∞ to, co chceme:
  96. transform(cisla.begin(), cisla.end(),
  97.           cisla.begin(), druha_mocnina);
  98. Anebo s pou₧itφm funktoru (musφme vytvo°it jeho instanci, nejlΘpe nepojmenovanou, pak mß toti₧ p°ekladaΦ volnΘ ruce k optimalizaci):
  99. transform(cisla.begin(), cisla.end(),
  100.           cisla.begin(), cDruhaMocnina());
  101. Prvnφ dva parametry jsou vstupnφ iterßtory, kterΘ ohraniΦujφ zpracovßvanΘ prvky, t°etφ parametr je v²stupnφ iterßtor - od toho mφsta se budou uklßdat v²sledky (v naÜem p°φpad∞ p°epφÜeme p∙vodnφ prvky). ╚tvrt²m parametrem je funkce, tj. to, co se mß provΘst s ka₧d²m prvkem v danΘm rozsahu - m∙₧e to b²t jak klasickß funkce, tak funktor. 
  102. Zde jeÜt∞ nejsou v²hody funktoru patrnΘ, ale zkusme se zamyslet nad tφm, jak funkci transform vnutit libovolnou mocninu. Pokud bychom trvali na pou₧itφ klasick²ch funkcφ, nezbylo by nic jinΘho, ne₧ pro ka₧d² p°φpad napsat zvlßÜtnφ funkci. To by pak vypadalo t°eba takto:
  103. int treti_mocnina(int cislo) { /* ... * / }
  104. int ctvrta_mocnina(int cislo) { /* ... */ }
  105. // ... a tak n∞jak po°ßd dßl, prost∞ hr∙za...
  106. NezkuÜen² programßtor by si te∩ asi °ekl: "Sbohem STL, vracφm se ke smyΦkßm." My, kte°φ u₧ vφme o funktorech, ovÜem napφÜeme:
  107. class cMocnina
  108. {
  109. public:
  110.    cMocnina(int stupen)
  111.       : stupen_(stupen) {}
  112.    int operator()(int cislo) 
  113.       { /* ... */ }
  114. private:
  115.    int stupen_;
  116. };
  117. T°eba Üestß mocnina by pak vypadala takto:
  118. transform(cisla.begin(), cisla.end(),
  119.           cisla.begin(), cMocnina(6));
  120. V²hoda funktoru je jasnß: Proto₧e to je (jakkoli slo₧itß) t°φda, m∙₧e obsahovat libovolnß data, kterß m∙₧eme (nebo musφme) inicializovat v konstruktoru. Tato data mohou "parametrizovat" danou funkci, v naÜem p°φpad∞ mocninu. Tohle obyΦejnΘ funkce neum∞jφ (alespo≥ ne tak elegantn∞).
  121. TypovΘ informace
  122. DalÜφ d∙le₧itou vlastnostφ funktor∙ je, ₧e mohou obsahovat vno°enΘ definice typ∙. Tyto definice mohou nap°φklad definovat typ vracenΘ hodnoty nebo typy argument∙. Tentokrßt budeme pro zm∞nu hledat prvnφ zßpornΘ Φφslo v posloupnosti cisla (pou₧itφm algoritmu find_if), a pou₧ijeme navφc obecn∞jÜφ funktor, kter² bude zjiÜ¥ovat, zda danß hodnota je menÜφ ne₧ n∞jakΘ p°edem zadanΘ Φφslo. A kdy₧ u₧ jsme v tom, implementujeme funktor jako Üablonu.
  123. template <class T> class cJeMensiNez
  124. {
  125. public:
  126.    // typ v²sledku
  127.    typedef bool result_type;
  128.    // typ argumentu
  129.    typedef T argument_type;
  130.  
  131.    cJeMensiNez(T co) : co_(co) {}
  132.    result_type operator()(T cislo) 
  133.       { return cislo < co_; }
  134. private:
  135.    T co_;
  136. };
  137.  
  138. iterator prvni_zaporne = 
  139.    find_if(cisla.begin(), cisla.end(),
  140.            cJeMensiNez<int>(0));
  141. JmΘna pro vno°enΘ typy nebyla, jak poslΘze uvidφme, zvolena nßhodn∞. P°ipome≥me jeÜt∞, ₧e algoritmus find_if vrßtφ iterßtor cisla.end() v p°φpad∞, ₧e nenalezne ₧ßdnΘ zßpornΘ Φφslo.
  142. Otßzka znφ: Co s t∞mi vno°en²mi definicemi typ∙? Nenφ to zbyteΦnost? Mo₧nß si jeÜt∞ vzpomenete na Φlßnky [4] a [5], kde jsme p°evßd∞li v²razy na data. NeÜlo o nic jinΘho ne₧ o aplikaci funktor∙. Sice jsme nepou₧φvali p°etφ₧en² operßtor (), ale statickou metodu apply, to je vÜak drobn² detail - myÜlenka byla stejnß. K tomu, abychom zjistili v²sledn² typ v²razu, jsme pot°ebovali prßv∞ ony vno°enΘ definice typ∙. Prßv∞ p°i pou₧itφ Üablon vynikne tato vlastnost funktor∙. Zkuste se obyΦejnΘ funkce zeptat na typ v²sledku nebo typy argument∙ - to prost∞ nejde.
  143. AdaptΘr
  144. U₧ umφme najφt prvnφ zßpornΘ Φφslo v n∞jakΘ posloupnosti. Dokßzali bychom najφt takΘ prvnφ nezßpornΘ Φφslo v posloupnosti? Asi nßs jako prvnφ napadne vzφt a zkopφrovat p°edchozφ p°φklad, zm∞nit jmΘno t°φdy a zm∞nit znamΘnko < na >=. V₧dy¥ p°ece jde jen o negaci toho, co jsme m∞li p°edtφm. Jist∞₧e, ale do stejnΘ situace (kdy pot°ebujeme negaci) se m∙₧eme dostat vφcekrßt a urΦit∞ by nebylo nejlepÜφ poka₧dΘ kopφrovat a upravovat, zvlßÜt∞ pak v p°φpad∞ hodn∞ slo₧itΘho funktoru (takovΘ v tomto Φlßnku z pochopiteln²ch d∙vod∙ nenajdete, ale v praxi na n∞co takovΘho urΦit∞ narazφte). 
  145. Tak₧e jak na to? Nevyhovuje nßm p°etφ₧en² operßtor (), pot°ebujeme toti₧ jeho negaci. Obecn∞ °eΦeno, chceme jej pro naÜe po₧adavky p°izp∙sobit (adaptovat) - prost°edek, kter² nßm k tomu poslou₧φ, se proto naz²vß adaptΘr. O co jde, naznaΦφ schΘma na obr. 2 a nßsledujφcφ struΦn² popis.
  146. Klient chce pou₧φt funktor, ale ten nespl≥uje jeho po₧adavky nebo mu prost∞ nerozumφ (analogie s jazykovou bariΘrou je docela v²sti₧nß). Klient proto pot°ebuje n∞jakΘho prost°ednφka (tlumoΦnφka); adaptΘr je pro takovou ·lohu jako stvo°en².
  147. V naÜem p°φpad∞ je klientem funkce find_if a funktorem Üablonovß t°φda cJeMensiNez. Funkce find_if sice vφ, co d∞lat s t°φdou cJeMensiNez, ale tato t°φda nespl≥uje po₧adavky pro hledßnφ nezßporn²ch Φφsel. Pom∙₧e adaptΘr:
  148. template <class FUNKTOR> class cNegace
  149. {
  150. public:
  151.    typedef typename
  152.       FUNKTOR::result_type result_type;
  153.    typedef typename
  154.       FUNKTOR::argument_type argument_type;
  155.  
  156.    cNegace(FUNKTOR fc) : func_(fc) {}
  157.    result_type operator()(argument_type x) 
  158.       { return !func_(x); }
  159. private:
  160.    FUNKTOR func_;
  161. };
  162. AdaptΘr jsme zde napsali dostateΦn∞ obecn∞, tak₧e dokß₧e znegovat tΘm∞° jakoukoli unßrnφ funkci. Vynikne zde pou₧itφ vno°en²ch typ∙ - p°edem toti₧ nevφme, co mß funktor vracet a jakΘho typu je jeho argument. M∙₧eme se ale "zeptat" - to jsou ty prvnφ dv∞ deklarace typedef. AdaptΘr si takΘ  "pro vlastnφ pot°ebu" vytvß°φ kopii funktoru. Je tedy nutnΘ, aby funktor podporoval kopφrovßnφ a p°i°azenφ, a m∞l by se proto chovat jako hodnota (value semantics). Net°eba snad zd∙raz≥ovat, ₧e takto vytvo°en² adaptΘr je rovn∞₧ funktor.
  163. Prvnφ nezßpornΘ Φφslo v danΘ posloupnosti zφskßme tedy takto:
  164. iterator prvni_nezaporne = 
  165.    find_if(cisla.begin(), cisla.end(), 
  166.       cNegace<cJeMensiNez<int> >(
  167.                   cJeMensiNez<int>(0)));
  168. Tento zßpis u₧ je pon∞kud slo₧it∞jÜφ, proto si ukß₧eme, jak jej zp°ehlednit. Slo₧itost plyne z toho, ₧e p°i vytvß°enφ instance adaptΘru musφme uvΘst typ instance, co₧ je:
  169. cNegace<cJeMensiNez<int> >
  170. T∞₧ko si p°edstavit, jak by to vypadalo, kdybychom m∞li takto vytvo°it "adaptΘr adaptΘru adaptΘru funktoru" (v praxi v∞c celkem b∞₧nß). NaÜt∞stφ jsou tu pokroΦilΘ vlastnosti C++, o kter²ch sice ani nemusφte v∞d∞t, ale neÜkodφ si je p°ipomenout - jde o dedukci Üablonov²ch argument∙ u Üablonov²ch funkcφ. V praxi to vypadß tak, ₧e vytvo°φme "vytvo°ujφcφ" funkci:
  171. template <class FUNKTOR>
  172. cNegace<FUNKTOR> negace(const FUNKTOR & fc)
  173. {
  174.    return cNegace<FUNKTOR>(fc);
  175. }
  176. To je funkce, jejφm₧ jedin²m ·kolem je vytvo°it p°φsluÜnou instanci funktoru nebo adaptΘru funktoru. Dφky nφ u₧ m∙₧eme psßt pouze:
  177. iterator prvni_nezaporne = 
  178.    find_if(cisla.begin(), cisla.end(),
  179.       negace(cJeMensiNez<int>(0)));
  180. èablonovß vytvo°ujφcφ funkce negace si toti₧ na zßklad∞ typu svΘho argumentu, jφm₧ je instance t°φdy cJeMensiNez<int>, dokß₧e vydedukovat Üablonov² argument. Zjistφ tak, ₧e FUNKTOR je vlastn∞ cJeMensiNez<int>, a vytvo°φ instanci p°φsluÜnΘho adaptΘru.
  181. A proΦ jsme tvrdili, ₧e nßÜ adaptΘr dokß₧e adaptovat tΘm∞° jakoukoli unßrnφ funkci? D∙vodem pro pou₧itφ sl∙vka tΘm∞° jsou vno°enΘ definice typ∙. Adaptovanß funkce musφ mφt vno°enΘ definice typ∙ s pevn∞ dan²mi jmΘny. Vypadß to tedy na znaΦnΘ omezenφ, ale jen na prvnφ pohled. Pokud funkce nemß ony vno°enΘ definice typ∙, nebo je mß, ale jmenujφ se jinak, nic nßm nebrßnφ, abychom pro tuto funkci napsali specißlnφ adaptΘr, kter² to napravφ. (Takovou funkci tedy musφme adaptovat nadvakrßt.) Nßsledujφcφ sekce prozradφ, jak na to.
  182. AdaptΘr obyΦejnΘ funkce
  183. Nßmi vytvo°en² adaptΘr cNegace nedokß₧e adaptovat obyΦejnΘ funkce, proto₧e se u nich neumφ "zeptat" na typ vracenΘ hodnoty nebo typ parametru - nenajde vno°enΘ definice typ∙. NaÜt∞stφ i tady existuje °eÜenφ - adaptΘr (jak jinak...). Budeme prost∞ adaptovat funkci na n∞co, co lze dßle adaptovat...
  184. // adaptΘr libovolnΘ unßrnφ klasickΘ funkce
  185. template <class ARGUMENT, class VYSLEDEK>
  186. class cFunkce
  187. {
  188. public:
  189.    typedef VYSLEDEK result_type;
  190.    typedef ARGUMENT argument_type;
  191.  
  192.    cFunkce(VYSLEDEK (*pfc)(ARGUMENT))
  193.       : pfunc_(pfc) {}
  194.    VYSLEDEK operator()(ARGUMENT x) 
  195.       { return pfunc_(x); }
  196. private:
  197.    VYSLEDEK (*pfunc_)(ARGUMENT);
  198. };
  199.  
  200. // vytvo°ujφcφ funkce
  201. template <class ARGUMENT, class VYSLEDEK> 
  202. cFunkce<ARGUMENT, VYSLEDEK> 
  203. ptr_funkce(VYSLEDEK (*pf)(ARGUMENT))
  204. {
  205.    return cFunkce<ARGUMENT, VYSLEDEK>(pf);
  206. }
  207. Nynφ u₧ mßme prost°edky na adaptovßnφ (adaptovan²ch) obyΦejn²ch funkcφ. Snad to trochu osv∞tlφ nßsledujφcφ p°φklad. Hledejme te∩ znovu prvnφ zßpornΘ Φφslo za pou₧itφ funkce
  208. bool mensi_nez_nula(int x)
  209.    { return x < 0; }
  210. Mßme t°i mo₧nosti (v podstat∞ ekvivalentnφ): bez adaptΘru, s adaptΘrem bez vytvo°ujφcφ funkce a s adaptΘrem za pomoci vytvo°ujφcφ funkce. Podφvejme se na poslednφ:
  211. iterator prvni_zaporne = 
  212.    find_if(cisla.begin(), cisla.end(), 
  213.       ptr_funkce(mensi_nez_nula));
  214. Nynφ se zam∞°φme znovu na prvnφ nezßpornΘ Φφslo. S pou₧itφm vytvo°ujφcφch funkcφ m∙₧eme napsat:
  215. iterator prvni_nezaporne = 
  216.    find_if(cisla.begin(), cisla.end(), 
  217.       negace(ptr_funkce(mensi_nez_nula)));
  218. Tentokrßt u₧ to projde, nebo¥ adaptΘr cNegace adaptuje adaptΘr cFunkce (u₧ bylo °eΦeno, ₧e adaptΘr adaptΘru je b∞₧nß v∞c...) a ten mß vno°enΘ definice typ∙.
  219. Troufßm si tvrdit, ₧e to je elegantnφ a p°ehlednΘ. Nemusφte ani znßt detaily implementace vytvo°ujφcφch funkcφ Φi adaptΘr∙. StaΦφ jenom v∞d∞t, "co to d∞lß" a "jak se to napφÜe".
  220. Vazba argument∙
  221. N∞kdy pot°ebujeme z binßrnφ funkce ud∞lat unßrnφ. Jin²mi slovy: u funkce se dv∞ma parametry chceme jeden z nich zafixovat a zφskat tak vlastn∞ funkci s jednφm parametrem. I tady existuje elegantnφ °eÜenφ v podob∞ adaptΘru. Ukß₧eme si to na p°φkladu, v n∞m₧ op∞t hledßme prvnφ zßpornΘ Φφslo, ale tentokrßt mßme k dispozici binßrnφ funkci:
  222. bool mensi_nez(int x, int y) 
  223.    { return x < y; }
  224. Cht∞li bychom zafixovat druh² argument na hodnotu 0, resp. obecn∞ji na libovolnou danou hodnotu. Vytvo°φme tedy adaptΘr:
  225. // adaptΘr fixujφcφ druh² argument 
  226. // binßrnφ funkce bool fc(int, int)
  227. class cFixArg2
  228. {
  229. public:
  230.    cFixArg2(bool (*pfc)(int, int), int fixy) 
  231.       : pfunc_(pfc), fixy_(fixy) {}
  232.    bool operator()(int x) 
  233.       { return pfunc_(x, fixy_); }
  234. private:
  235.    bool (*pfunc_)(int, int);
  236.    int fixy_;
  237. };
  238. Finta spoΦφvß v tom, ₧e adaptΘr si "zapamatuje" zafixovanou hodnotu a p°i ka₧dΘm zavolßnφ operßtoru () ji "podstrΦφ" adaptovanΘ funkci jako druh² argument. Tento adaptΘr je tedy unßrnφ funktor (mß jen jeden parametr). Pou₧itφ by mohlo vypadat takto:
  239. iterator prvni_zaporne = 
  240.    find_if(cisla.begin(), cisla.end(), 
  241.            cFixArg2(mensi_nez, 0));
  242. To, co vidφte, je pouze vytvo°enφ instance adaptΘru - proto dva argumenty. O vlastnφ volßnφ tΘto (nepojmenovanΘ) instance se postarß funkce find_if. Äe to skuteΦn∞ funguje, se m∙₧ete op∞t p°esv∞dΦit v [7].
  243. Zpo₧d∞nΘ volßnφ
  244. Funkce je obyΦejn² "kus k≤du" a jedinΘ, co s nφ m∙₧eme ud∞lat (krom∞ zφskßnφ adresy), je zavolat ji. Naproti tomu funktor je t°φda. Nejprve musφme vytvo°it instanci a pak teprve m∙₧eme volat p°etφ₧en² operßtor (). Nikde vÜak nenφ °eΦeno, ₧e ob∞ akce musφ prob∞hnout bezprost°ednΘ po sob∞. M∙₧eme vytvo°it instanci funktoru, n∞kam si ji ulo₧it, a a₧ se nßm bude hodit, zavolat ji (p°esn∞ji: jejφ p°etφ₧en² operßtor ()). OznaΦujeme to jako zpo₧d∞nΘ volßnφ (delayed call).
  245. P°edstavme si, ₧e v n∞jakΘ aplikaci (textovΘm editoru) zpracovßvßme text. NaÜe jednotlivΘ akce se uklßdajφ jako funktory na zßsobnφk. Stejn∞ tak m∙₧eme zßrove≥ (na jin² zßsobnφk) uklßdat "antifunktory", tedy jakΘsi protiakce, kterΘ vracφ text do p∙vodnφho stavu. Takhle n∞jak by mohla vypadat implementace funkce Undo/Redo v textovΘm nebo libovolnΘm jinΘm editoru.
  246. Na dalÜφ aplikace zpo₧d∞n²ch volßnφ jist∞ p°ijdete sami. Je zde ale nutnΘ zmφnit dalÜφ po₧adavek na funktory vypl²vajφcφ z toho, ₧e jejich instance se n∞kam uklßdajφ. TakovΘ funktory se toti₧ musφ chovat jako hodnoty (value semantics). To mimo jinΘ znamenß, ₧e musφ mφt sprßvn∞ implementovßno p°i°azenφ a kopφrovßnφ.
  247.  
  248. P°φÜt∞
  249. Po pr∙prav∞, kterou jste prßv∞ absolvovali (a snad p°iÜli funktor∙m na chu¥), u₧ p°φÜt∞ nahlΘdneme do STL a p°edstavφme si funktory, kterΘ pro nßs tv∙rci jazyka p°ichystali. Dozvφte se takΘ, jak napsat vlastnφ funktor nebo adaptΘr tak, aby byly "kompatibilnφ" s STL, a nezapomeneme ani na nejΦast∞jÜφ chyby a "podrazy".
  250. Pokud vßs povφdßnφ o funktorech zaujalo a cht∞li byste v∞d∞t vφc, doporuΦuji nap°φklad [2]. Implementace funktor∙, kterou mß "na sv∞domφ" Üablonov² guru A. Alexandrescu, je skuteΦn∞ na ·rovni, nenφ vÜak kompatibilnφ s funktory z STL (co₧ v∙bec neznamenß, ₧e by byla horÜφ).
  251.  
  252. Jaroslav Fran∞k
  253.  
  254. infotipy
  255. [1] Mezinßrodnφ standard jazyka C++, ICO/IEC 14882:1998
  256. [2] A. Alexandrescu: Modern C++ Design, ISBN 0-201-70431-5, Kapitola 5, Generalized Functors (Zobecn∞nΘ funktory)
  257. [3] J. Fran∞k: Nebojte se STL, Chip 12/01
  258. [4] J. Fran∞k: èablony v²raz∙ v C++, Chip 4/01
  259. [5] J. Fran∞k: èablonovß magie, Chip 5/01
  260. [6] Dokumentace knihovny STL od SGI: www.sgi.com/tech/stl
  261. [7] Zdrojov² k≤d v rubrice Chip Plus na Chip CD 1/02
  262.  
  263.