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.
Funkce a funktor
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.
Klasickß funkce
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:
int druha_mocnina(int cislo)
{ return cislo * cislo; }
S klasick²mi funkcemi pracujeme bu∩ p°φmo, tj. pou₧ijeme identifikßtor funkce, nebo pomocφ ukazatel∙ Φi referencφ.
Operßtor
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φ:
* Umo₧≥ujφ zkrßcen² zßpis volßnφ bez kulat²ch zßvorek.
* 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.
* 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.).
VÜe objasnφ n∞kolik p°φklad∙.
int operator +(int, int);
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:
int a = 1, b = 2, c;
c = a + b;
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 +
sInt operator +(const sInt & a,
const sInt & b);
m∙₧eme psßt
sInt a(1), b(2), c;
c = a + b;
TakΘ bychom mohli napsat
c = operator +(a, b);
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φ.
╚lenskß funkce
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:
class cNeco
{
public:
int clenska_funkce();
private:
int soukroma_data;
};
int cNeco::clenska_funkce()
{ return soukroma_data; }
int main() {
sNeco a;
cout << a.clenska_funkce();
}
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:
int main() {
int (cNeco::* pf)() =
&cNeco::clenska_funkce;
sNeco a;
cout << (a.*pf)();
cout << ((&a)->*pf)();
}
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.
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.
Funktor
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:
class cDruhaMocnina
{
public:
int operator()(int cislo)
{ return cislo * cislo; }
};
Pou₧itφ je nasnad∞:
int a = 1, b;
cDruhaMocnina sqr; // instance
b = sqr(a); // pou₧itφ
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:
cDruhaMocnina * psqr = &sqr;
// dereferencovßnφ
b = (*psqr)(a);
// nezkrßcen² zßpis
b = psqr->operator()(a);
Co₧ je skoro jako p∞st na oko ve srovnßnφ s elegantnφm zßpisem sqr(a).
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.
Trocha motivace
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.
Funkce parametrem Üablony
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:
for ( int i = 0; i != N; ++i )
// N je velikost kontejneru
{
cisla[i] *= cisla[i];
}
My zkuÜen∞jÜφ bychom ale spφÜ pou₧ili (dop°ednΘ) iterßtory:
for ( iterator i = cisla.begin();
i != cisla.end(); ++i )
{
*i *= *i; // pozor na hv∞zdiΦky
}
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).
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:
transform(cisla.begin(), cisla.end(),
cisla.begin(), druha_mocnina);
Anebo s pou₧itφm funktoru (musφme vytvo°it jeho instanci, nejlΘpe nepojmenovanou, pak mß toti₧ p°ekladaΦ volnΘ ruce k optimalizaci):
transform(cisla.begin(), cisla.end(),
cisla.begin(), cDruhaMocnina());
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.
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:
int treti_mocnina(int cislo) { /* ... * / }
int ctvrta_mocnina(int cislo) { /* ... */ }
// ... a tak n∞jak po°ßd dßl, prost∞ hr∙za...
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:
class cMocnina
{
public:
cMocnina(int stupen)
: stupen_(stupen) {}
int operator()(int cislo)
{ /* ... */ }
private:
int stupen_;
};
T°eba Üestß mocnina by pak vypadala takto:
transform(cisla.begin(), cisla.end(),
cisla.begin(), cMocnina(6));
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∞).
TypovΘ informace
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.
template <class T> class cJeMensiNez
{
public:
// typ v²sledku
typedef bool result_type;
// typ argumentu
typedef T argument_type;
cJeMensiNez(T co) : co_(co) {}
result_type operator()(T cislo)
{ return cislo < co_; }
private:
T co_;
};
iterator prvni_zaporne =
find_if(cisla.begin(), cisla.end(),
cJeMensiNez<int>(0));
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.
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.
AdaptΘr
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).
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.
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².
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:
template <class FUNKTOR> class cNegace
{
public:
typedef typename
FUNKTOR::result_type result_type;
typedef typename
FUNKTOR::argument_type argument_type;
cNegace(FUNKTOR fc) : func_(fc) {}
result_type operator()(argument_type x)
{ return !func_(x); }
private:
FUNKTOR func_;
};
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.
Prvnφ nezßpornΘ Φφslo v danΘ posloupnosti zφskßme tedy takto:
iterator prvni_nezaporne =
find_if(cisla.begin(), cisla.end(),
cNegace<cJeMensiNez<int> >(
cJeMensiNez<int>(0)));
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:
cNegace<cJeMensiNez<int> >
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:
template <class FUNKTOR>
cNegace<FUNKTOR> negace(const FUNKTOR & fc)
{
return cNegace<FUNKTOR>(fc);
}
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:
iterator prvni_nezaporne =
find_if(cisla.begin(), cisla.end(),
negace(cJeMensiNez<int>(0)));
è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.
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.
AdaptΘr obyΦejnΘ funkce
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...
// adaptΘr libovolnΘ unßrnφ klasickΘ funkce
template <class ARGUMENT, class VYSLEDEK>
class cFunkce
{
public:
typedef VYSLEDEK result_type;
typedef ARGUMENT argument_type;
cFunkce(VYSLEDEK (*pfc)(ARGUMENT))
: pfunc_(pfc) {}
VYSLEDEK operator()(ARGUMENT x)
{ return pfunc_(x); }
private:
VYSLEDEK (*pfunc_)(ARGUMENT);
};
// vytvo°ujφcφ funkce
template <class ARGUMENT, class VYSLEDEK>
cFunkce<ARGUMENT, VYSLEDEK>
ptr_funkce(VYSLEDEK (*pf)(ARGUMENT))
{
return cFunkce<ARGUMENT, VYSLEDEK>(pf);
}
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
bool mensi_nez_nula(int x)
{ return x < 0; }
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φ:
iterator prvni_zaporne =
find_if(cisla.begin(), cisla.end(),
ptr_funkce(mensi_nez_nula));
Nynφ se zam∞°φme znovu na prvnφ nezßpornΘ Φφslo. S pou₧itφm vytvo°ujφcφch funkcφ m∙₧eme napsat:
iterator prvni_nezaporne =
find_if(cisla.begin(), cisla.end(),
negace(ptr_funkce(mensi_nez_nula)));
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∙.
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".
Vazba argument∙
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:
bool mensi_nez(int x, int y)
{ return x < y; }
Cht∞li bychom zafixovat druh² argument na hodnotu 0, resp. obecn∞ji na libovolnou danou hodnotu. Vytvo°φme tedy adaptΘr:
// adaptΘr fixujφcφ druh² argument
// binßrnφ funkce bool fc(int, int)
class cFixArg2
{
public:
cFixArg2(bool (*pfc)(int, int), int fixy)
: pfunc_(pfc), fixy_(fixy) {}
bool operator()(int x)
{ return pfunc_(x, fixy_); }
private:
bool (*pfunc_)(int, int);
int fixy_;
};
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:
iterator prvni_zaporne =
find_if(cisla.begin(), cisla.end(),
cFixArg2(mensi_nez, 0));
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].
Zpo₧d∞nΘ volßnφ
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).
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.
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φ.
P°φÜt∞
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".
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Üφ).
Jaroslav Fran∞k
infotipy
[1] Mezinßrodnφ standard jazyka C++, ICO/IEC 14882:1998
[2] A. Alexandrescu: Modern C++ Design, ISBN 0-201-70431-5, Kapitola 5, Generalized Functors (Zobecn∞nΘ funktory)
[3] J. Fran∞k: Nebojte se STL, Chip 12/01
[4] J. Fran∞k: èablony v²raz∙ v C++, Chip 4/01
[5] J. Fran∞k: èablonovß magie, Chip 5/01
[6] Dokumentace knihovny STL od SGI: www.sgi.com/tech/stl
[7] Zdrojov² k≤d v rubrice Chip Plus na Chip CD 1/02