Standard C++

Ukazatele na členské funkce

Je typ „pointer-to-member-function“ jiný než „pointer-to-function“?

Ano.

Podívejme se na následující funkci:

int f(char a, float b);

Typ této funkce se liší podle toho, zda je to obyčejná funkce nebo nestatic členská funkce nějaké třídy:

Poznámka: pokud je to static členská funkce class Fred, její typ je stejný, jako kdyby to byla obyčejná funkce: „int (*)(char,float)„.

Jak předat ukazatel na členskou funkci obsluze signálu, zpětnému volání události X, systémovému volání, které spouští vlákno/úkol atd.

Nemůžete.

Protože členská funkce nemá smysl bez objektu, na kterém ji lze vyvolat, nelze to udělat přímo (kdyby byl The X WindowSystem přepsán v C++, pravděpodobně by se předávaly odkazy na objekty, ne jen ukazatele na funkce;objekty by samozřejmě ztělesňovaly požadovanou funkci a pravděpodobně i spoustu dalších věcí).

Jako záplatu pro existující software použijte funkci nejvyšší úrovně (nečlenu) jako obal, který přebírá objekt získanýjiným způsobem. V závislosti na rutině, kterou voláte, může být tato „jiná technika“ triviální nebo můževyžadovat trochu práce z vaší strany. Například systémové volání, které spouští vlákno, může vyžadovat předání ukazatele na funkci spolu s void*, takže můžete předat ukazatel na objekt v void*. Mnoho operačních systémů reálného času dělá něco podobného pro funkci, která spouští novou úlohu. V nejhorším případě můžete ukazatel na objekt uložit do globální proměnné; to může být vyžadováno u obsluhy unixových signálů (ale globální proměnné jsou obecně nežádoucí). V každém případě by funkce nejvyšší úrovně zavolala požadovanou členskou funkci objektu.

Tady je příklad nejhoršího případu (použití globálu). Předpokládejme, že chcete zavolat Fred::memberFn() na přerušení:

Poznámka: staticčlenské funkce nevyžadují k vyvolání skutečný objekt, sopointers-to-static-členské funkce jsou obvykle typově kompatibilní s běžnými pointers-to-functions. Nicméně,i když to pravděpodobně funguje ve většině překladačů, ve skutečnosti by to musela být extern "C" nečlenská funkce, aby to bylo správné, protože „propojení s C“ nezahrnuje pouze věci, jako je mangling jmen, ale také konvence volání, které se mohou lišit mezi C a C++.

Proč se mi stále objevují chyby při kompilaci (typová neshoda), když se snažím použít členskou funkci jako rutinu obsluhy přerušení?

Toto je speciální případ předchozích dvou otázek, proto si nejprve přečtěte předchozí dvě odpovědi.

Ne-staticčlenské funkce mají skrytý parametr, který odpovídá thisukazateli. Ukazatel this ukazuje na instanční data objektu. Hardware/firmware pro přerušení v systému není schopen poskytnout argument ukazatelethis. Jako obslužné rutiny přerušení musíte použít „normální“ funkce (nečlenové třídy) nebo členské funkce static.

Jedním z možných řešení je použít jako obslužnou rutinu přerušení člen static a nechat tuto funkci někde vyhledat dvojici instance/člen, která má být při přerušení zavolána. Výsledkem tedy je, že členská funkce jevyvolána při přerušení, ale z technických důvodů je třeba nejprve zavolat zprostředkující funkci.

Proč mám problém s převzetím adresy funkce C++?

Krátká odpověď: Pokud se ji snažíte uložit do ukazatele na funkci (nebo ji předat jako ukazatel na funkci), pak je to problém – to je důsledek předchozího FAQ.

Dlouhá odpověď: V C++ mají členské funkce implicitní parametr, který ukazuje na objekt (this ukazateluvnitř členské funkce). Normální funkce v C lze považovat za funkce s jinou konvencí volání než členské funkce, takže typy jejich ukazatelů (ukazatel na členskou funkci vs. ukazatel na funkci) jsou odlišné a nekompatibilní. C++ zavádí nový typ ukazatele, nazývaný ukazatel na člen, který lze vyvolat pouze poskytnutím objektu.

POZNÁMKA: nepokoušejte se „castovat“ ukazatel na člen-funkci na ukazatel na funkci; výsledek je nedefinovaný a pravděpodobně katastrofální. Např. pointer-to-member-function nemusí obsahovat strojovou adresu příslušné funkce. Jak bylo řečeno v minulém příkladu, pokud máte ukazatel na běžnou funkci jazyka C, použijte buď funkci na úrovni atopu (nečlena), nebo členskou funkci static (třídy).

Jak se vyhnout syntaktickým chybám při vytváření ukazatelů na členy?

Použijte typedef.

Jo, jasně, já vím: vy jste jiný. Jste chytří. Tyhle věci zvládneš i bez typedef. Povzdechněte si. Dostal jsem mnoho e-mailů od lidí, kteří se stejně jako vy odmítli řídit jednoduchými radami z tohoto FAQ. Ztratili hodiny a hodiny svého času, když by jim 10 vteřin typedef zjednodušilo život. Navíc, přiznejte si, že nepíšete kód, který si můžete přečíst jen vy; doufám, že píšete svůj kód, který si budou moci přečíst i ostatní – když budou unavení – když budou mít své vlastní termíny a své vlastní výzvy. Proč tedy záměrně ztěžovat život sobě i ostatním? Buďte chytří: používejte typedef.

Tady je ukázková třída:

class Fred {public: int f(char x, float y); int g(char x, float y); int h(char x, float y); int i(char x, float y); // ...};

Typová definice je triviální:

typedef int (Fred::*FredMemFn)(char x, float y); // Please do this!

To je vše! FredMemFn je jméno typu a ukazatel tohoto typu ukazuje na libovolný člen Fred, který přebírá (char,float), například Fred f, g, h a i.

Je pak triviální deklarovat ukazatel na členskou funkci:

int main(){ FredMemFn p = &Fred::f; // ...}

A je také triviální deklarovat funkce, které ukazatele na členské funkce přijímají:

void userCode(FredMemFn p){ /*...*/ }

A je také triviální deklarovat funkce, které ukazatele na členské funkce vracejí:

FredMemFn userCode(){ /*...*/ }

Takže prosím, používejte typedef. Buď to, nebo mi neposílejte e-maily o problémech, které máte s ukazateli na členské funkce!“

Jak se vyhnout syntaktickým chybám při volání členské funkce pomocí ukazatele na členskou funkci?

Pokud máte přístup k překladači a standardní knihovně, které implementují příslušné části připravovaného standardu C++17, použijte std::invoke. V opačném případě použijte makro #define.

Prosím.

Prosím.

Dostávám příliš mnoho e-mailů od zmatených lidí, kteří tuto radu odmítli přijmout. Je to tak jednoduché. Já vím, nepotřebujetestd::invoke ani makro a odborník, se kterým jste mluvili, to zvládne i bez nich, ale prosím, nenechte své ego, aby se postavilo do cesty tomu, co je důležité: penězům. Váš kód budou muset číst/udržovat další programátoři. Ano, já vím: jste chytřejší než všichni ostatní; fajn. A jste úžasní; fajn. Ale nepřidávejte do svého kódu zbytečnou složitost.

Použití std::invoke je triviální. Poznámka: FredMemFn je typedef pro typ ukazatele na člen:

Pokud nemůžete použít std::invoke, snižte náklady na údržbu tím, že v tomto konkrétním případě paradoxně použijete makro #define.

(Normálně nemám rád makra #define, ale u ukazatelů na členy byste je měli používat, protože zlepšují čitelnost a zapisovatelnost tohoto druhu kódu)

Makro je triviální:

#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))

Použití makra je také triviální. Poznámka: FredMemFn je typedef pro ukazatel na členský typ:

Důvod, proč je std::invoke nebo toto makro dobrý nápad, je ten, že volání členských funkcí je často mnohem složitější než právě uvedený jednoduchý příklad. Rozdíl ve čitelnosti a zapisovatelnosti je značný.comp.lang.c++ musel přetrpět stovky a stovky příspěvků od zmatených programátorů, kteří nedokázali správně pochopit syntaxi. Téměř všechny tyto chyby by zmizely, kdyby použili std::invoke nebo výše uvedené makro.

Poznámka: makra #define jsou zlá čtyřmi různými způsoby: zlá#1,zlá#2, zlá#3 a zlá#4. Ale přesto jsou někdy užitečná. Přesto byste po jejich použití měli cítit nejasný pocit studu.

Jak vytvořím a použiji pole pointer-to-member-function?

Použijte obě dříve popsaná makra typedef a std::invoke nebo #define a máte na 90 % hotovo.

Krok 1: vytvořte typedef:

Krok 2: vytvořte makro #define, pokud nemáte std::invoke:

#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))

Teď je vaše pole ukazatelů na členské funkce jednoduché:

FredMemFn a = { &Fred::f, &Fred::g, &Fred::h, &Fred::i };

A vaše použití jednoho z ukazatelů na členské funkce je také jednoduché:

void userCode(Fred& fred, int memFnNum){ // Assume memFnNum is between 0 and 3 inclusive: std::invoke(a, fred, 'x', 3.14);}

nebo pokud nemáte std::invoke,

void userCode(Fred& fred, int memFnNum){ // Assume memFnNum is between 0 and 3 inclusive: CALL_MEMBER_FN(fred, a) ('x', 3.14);}

Poznámka: makra #define jsou zlá čtyřmi různými způsoby: evil#1,evil#2, evil#3 a evil#4. Ale přesto jsou někdy užitečná. Stydět se, cítit se provinile, ale když taková zlá konstrukce, jako je makro, zlepší váš software, použijte ji.

Jak deklarovat ukazatel na členskou funkci, který ukazuje na členskou funkci const?

Krátká odpověď: Přidejte const napravo od ), když používáte typedef pro deklaraci členské funkce-pointertype.

Předpokládejme například, že chcete ukazatel na členskou funkci, který ukazuje na Fred::f, Fred::g nebo Fred::h:

class Fred {public: int f(int i) const; int g(int i) const; int h(int j) const; // ...};

Pak by to při použití typedef pro deklaraci typu ukazatel-členská funkce mělo vypadat takto:

To je ono!

Pak můžete deklarovat/předávat/vracet ukazatele členských funkcí jako obvykle:

Jaký je rozdíl mezi operátory .* a ->*?

Tomu nemusíte rozumět, pokud pro volání ukazatelů členských funkcí používáte std::invoke nebo makro. Ohyea, v tomto případě prosím používejte std::invoke nebo makro. A už jsem se zmínil, že byste v tomtopřípadě měli použít std::invoke nebo makro?!!!

Například:

Prosím, zvažte místo toho použití std::invoke nebo makra:

void sample(Fred x, Fred& y, Fred* z, FredMemFn func){ std::invoke(func, x, 42, 3.14); std::invoke(func, y, 42, 3.14); std::invoke(func, *z, 42, 3.14);}

nebo

Jak už bylo řečeno, reálná volání jsou často mnohem složitější než zdejší jednoduchá, takže použití std::invoke nebo makra obvykle zlepší zápis a čitelnost vašeho kódu.

Můžu převést ukazatel na členskou funkci na void*?

Ne!“

Technické detaily: Ukazatele na členské funkce a ukazatele na data nemusí být nutně reprezentovány stejným způsobem. Ukazatel na členskou funkci může být spíše datovou strukturou než jednoduchým ukazatelem. Přemýšlejte o tom: pokud ukazuje na virtuální funkci, nemusí ve skutečnosti ukazovat na staticky rozlišitelnou hromadu kódu, takže to ani nemusí být normální adresa – může to být nějaká jiná datová struktura.

Prosím, nepište mi, pokud se vám zdá, že výše uvedené funguje na vaší konkrétní verzi vašeho konkrétního překladače na vašem konkrétním operačním systému. Je mi to jedno. Je to nelegální, tečka.

Můžu převést ukazatel na funkci na void*?

Ne!

Technické detaily: void* Ukazatele jsou ukazatele na data a ukazatele na funkce ukazují na funkce. Jazyk nevyžaduje, aby funkce a data byly ve stejném adresovém prostoru, takže příkladem, nikoli omezením, na architekturách, které je mají v různých adresových prostorech, nebudou tyto dva různé typy ukazatelů srovnatelné.

Prosím, nepište mi, pokud se vám zdá, že výše uvedené funguje na vaší konkrétní verzi vašeho konkrétního překladače na vašem konkrétním operačním systému. Je mi to jedno. Je to nelegální, tečka.

Potřebuji něco jako function-pointers, ale s větší flexibilitou a/nebo bezpečností vláken; existuje jiný způsob?

Použijte functionoid.

Co je to sakra functionoid a proč bych ho měl používat?

Functionoidy jsou funkce na steroidech. Funkcioidy jsou přísně vzato výkonnější než funkce a tato dodatečná síla řeší některé (ne všechny) problémy, kterým obvykle čelíte, když používáte ukazatele funkcí.

Pracujme na příkladu, který ukazuje tradiční použití ukazatelů funkcí, a pak tento příklad převedeme na funkcioidy. Tradiční myšlenka ukazatelů funkcí spočívá v tom, že máte několik kompatibilních funkcí:

int funct1( /*...params...*/ ) { /*...code...*/ }int funct2( /*...params...*/ ) { /*...code...*/ }int funct3( /*...params...*/ ) { /*...code...*/ }

Pak k nim přistupujete pomocí ukazatelů funkcí:

typedef int(*FunctPtr)( /*...params...*/ );void myCode(FunctPtr f){ // ... f( /*...args-go-here...*/ ); // ...}

Někdy lidé vytvoří pole těchto ukazatelů funkcí:

FunctPtr array;array = funct1;array = funct1;array = funct3;array = funct2;// ...

V takovém případě volají funkci přístupem k poli:

array( /*...args-go-here...*/ );

Při použití functionoidů se nejprve vytvoří základní třída s čistě virtuální metodou:

Poté se místo tří funkcí vytvoří tři odvozené třídy:

Poté se místo předání ukazatele funkce předá Funct*. Vytvořím typedef s názvem FunctPtr jen proto, aby byl zbytek kódu podobný staromódnímu přístupu:

typedef Funct* FunctPtr;void myCode(FunctPtr f){ // ... f->doit( /*...args-go-here...*/ ); // ...}

Téměř stejným způsobem můžete vytvořit pole:

To nám dává první náznak toho, v čem jsou functionoidy přísně vzato výkonnější než ukazatele funkcí: skutečnost, že přístup s functionoidy má argumenty, které můžete předat ctorům (zobrazené výše jako …ctor-args…), zatímco verze s ukazateli funkcí nikoli. Představte si objekt functionoid jako zmrazené volání funkce (důraz na slovo volání). Na rozdíl od ukazatele na funkci je functionoid (pojmově) ukazatelem na částečně volanou funkci. Představte si na chvíli technologii, která vám umožní předat funkci některé, ale ne všechny argumenty, a pak vám umožní toto (částečně dokončené) volání zmrazit. Představte si, že tato technologie vám vrátí jakýsi magický ukazatel na toto zmrazené částečně dokončené volání funkce. Později pak pomocí tohoto ukazatele předáte zbývající argumenty a systém zázračně vezme vaše původní argumenty (které byly zmrazeny), spojí je se všemi lokálními proměnnými, které funkce vypočítala před zmrazením, to vše spojí s nově předanými argumenty a pokračuje v provádění funkce tam, kde skončila, když byla zmrazena. To může znít jako science fiction, ale koncepčně vám to functionoidy umožňují. Navíc umožňují opakovaně „dokončit“ tuto zmrazenou funkci s různými „zbývajícími parametry“, jak často chcete. Navíc umožňují (nikoli vyžadují), abyste při volání změnili stavfreeze-dried, což znamená, že si functionoidy mohou pamatovat informace od jednoho volání k druhému.

Postavme se opět nohama na zem a na několika příkladech si vysvětlíme, co to všechno blábolení vlastně znamená.

Předpokládejme, že původní funkce (ve staromódním stylu function-pointer) měly trochu jiné parametry.

int funct1(int x, float y){ /*...code...*/ }int funct2(int x, const std::string& y, int z){ /*...code...*/ }int funct3(int x, const std::vector<double>& y){ /*...code...*/ }

Když se parametry liší, staromódní přístup ve stylu ukazatelů funkcí je obtížně použitelný, protože volající neví, které parametry má předat (volající má pouze ukazatel na funkci, nikoliv jméno funkce nebo,když se parametry liší, počet a typy jejích parametrů) (nepište mi o tom e-mail; ano, můžete to udělat, ale musíte se postavit na hlavu a dělat nepřehledné věci; ale nepište mi o tom – použijte raději funkčníidy).

S functionoidy je situace, alespoň někdy, mnohem lepší. Vzhledem k tomu, že functionoid si lze představit jako mrazivé volání funkce, stačí vzít neobvyklé argumenty, například ty, které jsem nazval y a/nebo z, a udělat z nichargs příslušných ctorů. Můžete také předat společné argumenty (v tomto případě int s názvem x) do ctoru, ale nemusíte – místo toho máte možnost je předat čistě virtuální metodě doit(). Předpokládám, že chcete předat x do doit() a y a/nebo z do ctoru:

class Funct {public: virtual int doit(int x) = 0;};

Pak namísto tří funkcí vytvoříte tři odvozené třídy:

Teď vidíte, že parametry ctoru se při vytváření pole funkčníchoidů zmrazí do funkčníhooidu:

Takže když uživatel vyvolá doit() na jednom z těchto functionoidů, dodá „zbývající“ argumenty a voláníkoncepčně spojí původní argumenty předané ctoru s argumenty předanými do metody doit():

array->doit(12);

Jak jsem již naznačil, jednou z výhod functionoidů je, že můžete mít ve svém poli několik instancí, řekněme Funct1, a tyto instance mohou mít v sobě zmrazeny různé parametry. Například array aarray jsou obě typu Funct1, ale chování array->doit(12) se bude lišit od chováníarray->doit(12), protože chování bude záviset jak na 12, které bylo předáno doit(), tak na argumentech předaných ctors.

Další výhoda functionoidů je patrná, pokud změníme příklad z pole functionoidů na lokálnífunctionoid. Abychom si připravili půdu, vraťme se ke staromódnímu přístupu s funkčním ukazatelem a představme si, že se snažíte předat porovnávací funkci rutině sort() nebo binarySearch(). Rutina sort() nebo binarySearch() se jmenuje childRoutine() a typ porovnávací funkce-ukazatel se jmenuje FunctPtr:

void childRoutine(FunctPtr f){ // ... f( /*...args...*/ ); // ...}

Různí volající by pak předávali různé funkce-ukazatele podle toho, co by považovali za nejlepší:

void myCaller(){ // ... childRoutine(funct1); // ...}void yourCaller(){ // ... childRoutine(funct3); // ...}

Tento příklad můžeme snadno převést na příklad s použitím functionoidů:

Na pozadí tohoto příkladu vidíme dvě výhody functionoidů oproti function-pointerům. Výhodu „ctor args“ popsanou výše a navíc skutečnost, že functionoidy mohou udržovat stav mezi voláními způsobem bezpečným pro vlákna.U obyčejných function-pointerů lidé obvykle udržují stav mezi voláními pomocí statických dat. Statická data však nejsou ve své podstatě bezpečná pro vlákna – statická data jsou sdílena mezi všemi vlákny. Přístup functionoidů poskytuje něco, co je ze své podstaty bezpečné pro vlákna, protože kód končí s daty lokálními pro vlákna. Implementace je triviální: staromódní statický údaj změníme na instanční datový člen uvnitř objektu this functionoid a puf, data jsou nejen thread-local, ale jsou dokonce bezpečná i při rekurzivních voláních: každé volání yourCaller()bude mít svůj vlastní samostatný objekt Funct3 s vlastními samostatnými instančními daty.

Všimněte si, že jsme něco získali, aniž bychom něco ztratili. Pokud chcete data globální pro vlákno, functionoidy mohou dávat i youthat: stačí je změnit z instančního datového členu uvnitř objektu this functionoidu na statický datový členv rámci třídy functionoidu, nebo dokonce na statická data lokálního rozsahu. Nebyli byste na tom lépe než sfunkčními ukazateli, ale nebyli byste na tom ani hůře.

Přístup s functionoidem vám dává třetí možnost, která u staromódního přístupu není k dispozici: functionoid umožňuje volajícím rozhodnout, zda chtějí lokální nebo globální data vlákna. V případech, kdy by chtěli data globální pro vlákno, by byli zodpovědní za použití zámků, ale alespoň by měli možnost volby. Je to jednoduché:

Functionoidy neřeší všechny problémy, se kterými se setkáváme při tvorbě flexibilního softwaru, ale jsou přísně vzato výkonnější než ukazatele funkcí a stojí za to je alespoň zhodnotit. Ve skutečnosti můžete snadno dokázat, že functionoidy neztrácejí žádnou moc oproti function-pointers, protože si můžete představit, že staromódní přístup function-pointers jeekvivalentní tomu, že máme globální(!) objekt functionoid. Vzhledem k tomu, že vždy můžete vytvořit globální objekt typu functionoid, neztratili jste žádnou půdu pod nohama. QED.

Můžeš vytvářet functionoidy rychleji než normální volání funkcí?

Ano.

Pokud máš malý functionoid, a to je v reálném světě poměrně běžné, náklady na volání funkcí mohou být vysokéve srovnání s náklady na práci vykonanou functionoidem. V předchozím FAQ bylyfunkcioidyimplementovány pomocí virtuálních funkcí a obvykle vás budou stát volání funkce. Alternativnípřístup využívá šablony.

Následující příklad je v podobném duchu jako ten v předchozím FAQ. Přejmenoval jsem doit() naoperator()(), abych zlepšil čitelnost kódu volajícího a umožnil někomu předat běžný ukazatel na funkci:

Rozdíl mezi tímto přístupem a přístupem v předchozím FAQ je v tom, že fukcionoid se „naváže „na volajícího spíše v době kompilace než v době běhu. Představte si to jako předávání parametru: pokud v době kompilace víte, jaký druh funkceoidu chcete nakonec předat, pak můžete použít výše uvedenou techniku a můžete,alespoň v typických případech, získat výhodu rychlosti díky tomu, že kompilátorline-expanduje kód funkceoidu v rámci volajícího. Zde je příklad:

template <typename FunctObj>void myCode(FunctObj f){ // ... f( /*...args-go-here...*/ ); // ...}

Když kompilátor zkompiluje výše uvedený příklad, může inline-expandovat volání, což může zvýšit výkon.

Tady je jeden ze způsobů volání výše uvedeného:

void blah(){ // ... Funct2 x("functionoids are powerful", 42); myCode(x); // ...}

Mimochodem: jak bylo naznačeno v prvním odstavci výše, můžete také předat jména normálních funkcí (i když můžete nést náklady na volání funkce, když je volající použije):

void myNormalFunction(int x);void blah(){ // ... myCode(myNormalFunction); // ...}

Jaký je rozdíl mezi functionoidem a funktorem?

Functionoid je objekt, který má jednu hlavní metodu. Je to v podstatě OO rozšíření funkce typu C, jako je napříkladprintf(). Funkcioid se používá vždy, když má funkce více než jeden vstupní bod (tj. více než jednu „metodu“) a/nebo potřebuje udržovat stav mezi voláními způsobem bezpečným pro vlákna (přístup ve stylu C k udržování stavu mezi voláními je přidání lokální „statické“ proměnné do funkce, ale to je ve vícevláknovém prostředí strašně nebezpečné).

Funktor je speciální případ funkcioidu: je to funkcioid, jehož metoda je „operátor volání funkce, „operator()(). Protože přetěžuje operátor volání funkce, může kód volat jeho hlavní metodu pomocí stejné syntaxe, jakou by použil pro volání funkce. Např. pokud je „foo“ funktor, pro volání metody „operator()()“ na objektu „foo“ se řekne „foo()“. Výhodu to má v šablonách, protože pak šablona může mít parametr šablony, který bude použit jako funkce, a tento parametr může být buď jméno funkce, nebo objekt-funkce. Je zde výkonnostnívýhoda toho, že se jedná o funktor-objekt, protože metoda „operator()()“ může být inlineovaná (zatímco pokud předáte adresufunkce, musí být nutně neinlineovaná).

To je velmi užitečné pro věci, jako je funkce „comparison“ u setříděných kontejnerů. V C se porovnávací funkce vždy předává pomocí ukazatele (např, viz signatura k „qsort()“), ale v C++ může parametr přijít buď jako ukazatel na funkci, NEBO jako jméno funktoru-objektu, a výsledkem je, že tříděné kontejnery v C++ mohou být v některých případech mnohem rychlejší (a nikdy pomalejší) než ekvivalent v C.

Protože Java nemá nic podobného šablonám, musí pro všechny tyto věci používat dynamickou vazbu a dynamická vazba nutně znamená volání funkce. Normálně to není velký problém, ale v C++ chceme umožnit extrémně vysoký výkonkódů. To znamená, že C++ má filozofii „zaplať za to, jen když to použiješ“, což znamená, že jazyk nikdy nesmí libovolnězavádět žádnou režii nad to, co je fyzický stroj schopen vykonat (samozřejmě programátor může volitelně používattechniky, jako je dynamická vazba, které obecně zavedou určitou režii výměnou za flexibilitu nebo jinou „ilitu“, ale je na návrháři a programátorovi, aby se rozhodli, zda chtějí výhody (a náklady) takovýchkonstrukcí).

.

Leave a Reply