Standard C++

Pointer til medlemsfunktioner

Er typen af “pointer-to-member-function” forskellig fra “pointer-to-function”?

Ja.

Tænk på følgende funktion:

int f(char a, float b);

Typen af denne funktion er forskellig, alt efter om det er en almindelig funktion eller en ikkestatic medlemsfunktion i en eller anden klasse:

Bemærk: Hvis det er en static medlemsfunktion i class Fred, er dens type den samme, som hvis det var en almindelig funktion:”int (*)(char,float)“.

Hvordan overfører jeg en pointer-to-member-funktion til en signalhåndtering, X-hændelses-callback, systemkald, der starter en tråd/opgave osv?

Det skal du ikke.

Da en medlemsfunktion er meningsløs uden et objekt at påkalde den på, kan du ikke gøre det direkte (hvis X WindowSystem blev omskrevet i C++, ville det sandsynligvis videregive referencer til objekter rundt, ikke kun pointere til funktioner; naturligvis ville objekterne indeholde den nødvendige funktion og sandsynligvis en hel masse andet).

Som et plaster til eksisterende software kan man bruge en top-level (ikke-medlems)-funktion som en wrapper, der tager et objekt, der er opnået ved hjælp af en anden teknik. Afhængigt af den rutine, du kalder, kan denne “anden teknik” være triviel eller måske kræve lidt arbejde fra din side. Systemkaldet, der starter en tråd, kan f.eks. kræve, at du overdrager en funktionsviseren sammen med en void*, så du kan overdrage objektviseren i void*. Mange realtidsoperativsystemer gør noget lignende for den funktion, der starter en ny opgave. I værste fald kan du gemme objektpointeren i en global variabel; dette kan være nødvendigt for Unix-signalhåndteringsfunktioner (men globals er generelt uønskede). Under alle omstændigheder vil funktionen på øverste niveau kalde den ønskede medlemsfunktion på objektet.

Her er et eksempel på det værste tilfælde (ved hjælp af en global). Antag, at du vil kalde Fred::memberFn() på interrupt:

Bemærk: static-medlemsfunktioner kræver ikke et faktisk objekt for at blive påkaldt, sopointers-to-static-medlemsfunktioner er normalt type-kompatible med almindelige pointere-til-funktioner. Men selv om det sandsynligvis virker på de fleste compilere, skal det faktisk være en extern "C" non-member-funktion for at være korrekt, da “C-linkage” ikke kun dækker ting som navnemangling, men også kaldskonventioner, som kan være forskellige mellem C og C++.

Hvorfor får jeg hele tiden kompileringsfejl (type mismatch), når jeg forsøger at bruge en medlemsfunktion som en interrupt service rutine?

Dette er et specialtilfælde af de to foregående spørgsmål, læs derfor de to foregående svar først.

Non-static medlemsfunktioner har en skjult parameter, der svarer til this pointeren. this-pointeren peger på instansdataene for objektet. Systemets afbrydelseshardware/firmware er ikke i stand til at leverethis-pointerargumentet. Du skal bruge “normale” funktioner (ikke klassemedlemmer) eller static-medlemsfunktioner som afbrydelsesservicerutiner.

En mulig løsning er at bruge et static-medlem som afbrydelsesservicerutine og lade denne funktion søge et sted for at finde det instans/medlems-par, der skal kaldes ved afbrydelsen. Effekten er således, at en member-funktion bliver påkaldt ved en interrupt, men af tekniske årsager skal man først kalde en mellemliggende funktion.

Hvorfor har jeg problemer med at tage adressen på en C++-funktion?

Kort svar: Hvis du forsøger at gemme den i (eller videregive den som) en pointer-til-funktion, så er det problemet – dette er en konsekvens af den tidligere FAQ.

Langt svar: I C++ har medlemsfunktioner en implicit parameter, som peger på objektet (this-pointeren inden for medlemsfunktionen). Normale C-funktioner kan tænkes at have en anden kaldskonvention end medlemsfunktioner, så typerne af deres pointere (pointer-to-member-funktion vs. pointer-to-funktion) er forskellige og inkompatible. C++ introducerer en ny type pointer, kaldet pointer-to-member, som kun kan påberåbes ved at angive et objekt.

BEMÆRK: man må ikke forsøge at “caster” en pointer-to-member-funktion til en pointer-to-funktion; resultatet er udefineret og sandsynligvis katastrofalt. F.eks. er det ikke påkrævet, at en pointer-to-member-funktion indeholder maskinadressen for den relevante funktion. Som det blev sagt i det sidste eksempel, hvis du har en pointer til en almindelig C-funktion, skal du enten bruge en atop-level (non-member)-funktion eller en static (klasse)-medlemsfunktion.

Hvordan kan jeg undgå syntaksfejl, når jeg opretter pointere til medlemmer?

Brug en typedef.

Ja, ja, okay, jeg ved det godt: du er anderledes. Du er klog. Du kan gøre disse ting uden en typedef. Suk. Jeg har modtaget mange e-mails fra folk, der ligesom dig har nægtet at følge det enkle råd i denne FAQ. De har spildt time efter time af deres tid, når 10 sekunder med typedefs ville have forenklet deres liv. Og se det i øjnene, du skriver ikke kode, som kun du kan læse; du skriver forhåbentlig din kode, som andre også vil kunne læse – når de er trætte – når de har deres egne deadlines og deres egne udfordringer. Så hvorfor bevidst gøre livet sværere for dig selv og for andre? Vær smart: Brug en typedef.

Her er en eksempelklasse:

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); // ...};

Den typedef er triviel:

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

Det var det! FredMemFn er typenavnet, og en pointer af denne type peger på ethvert medlem af Fred, der tager(char,float), f.eks. Freds f, g, h og i.

Det er så trivielt at deklarere en member-funktions pointer:

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

Og det er også trivielt at deklarere funktioner, der modtager member-funktions pointere:

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

Og det er også trivielt at deklarere funktioner, der returnerer member-funktions pointere:

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

Så brug endelig en typedef. Enten det eller lad være med at sende mig e-mail om de problemer, du har med dine member-funktions-pointere!

Hvordan kan jeg undgå syntaksfejl, når jeg kalder en member-funktion ved hjælp af en pointer-to-member-funktion?

Hvis du har adgang til en compiler og et standardbibliotek, der implementerer de relevante dele af den kommende C++17-standard, skal du bruge std::invoke. Ellers skal du bruge en #define-makro.

Please.

Pretty please.

Jeg får alt for mange e-mails fra forvirrede personer, der har nægtet at følge dette råd. Det er så enkelt. Jeg ved godt, at du ikke har brug forstd::invoke eller en makro, og den ekspert, du talte med, kan gøre det uden nogen af dem, men lad ikke dit ego komme i vejen for det, der er vigtigt: penge. Andre programmører vil skulle læse/vedligeholde din kode. Ja, jeg ved det godt: du er klogere end alle andre; fint nok. Og du er fantastisk; fint. Men lad være med at tilføje unødvendig kompleksitet til din kode.

Brug af std::invoke er trivielt. Bemærk: FredMemFn er en typedef for en pointer-to-membertype:

Hvis du ikke kan bruge std::invoke, kan du reducere vedligeholdelsesomkostningerne ved, paradoksalt nok, at bruge en #define-makro i dette særlige tilfælde.

(Normalt bryder jeg mig ikke om #define-makroer, men du bør bruge dem med pointers tomembers, fordi de forbedrer læsbarheden og skrivbarheden af den slags kode.)

Makroen er triviel:

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

Brug af makroen er også triviel. Bemærk: FredMemFn er en typedef for en pointer-to-membertype:

Grunden til, at std::invoke eller denne makro er en god idé, er, at memberfunktionsinvokationer ofte er meget mere komplekse end det simple eksempel, der lige er givet. Forskellen i læsbarhed og skrivbarhed er betydelig.comp.lang.c++ har måttet udholde hundreder og atter hundreder af indlæg fra forvirrede programmører, som ikke helt kunne få syntaksen til at passe. Næsten alle disse fejl ville være forsvundet, hvis de havde brugt std::invoke eller ovenstående makro.

Bemærk: #define-makroer er onde på 4 forskellige måder: onde#1,onde#2, onde#3 og onde#4. Men de er stadig nyttige nogle gange. Men du bør stadig føle en vag følelse af skam efter at have brugt dem.

Hvordan opretter og bruger jeg et array af pointer-to-member-function?

Brug både typedef og std::invoke eller makroen #define beskrevet tidligere, og du er 90 % færdig.

Stræk 1: Opret en typedef:

Stræk 2: Opret en #define-makro, hvis du ikke har std::invoke:

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

Nu er dit array af pointers-to-member-funktioner lige til at gå til:

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

Og din brug af en af medlemsfunktions-pointerne er også ligetil:

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

eller hvis du ikke har std::invoke,

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

Bemærk: #define-makroer er onde på fire forskellige måder: onde#1,onde#2, onde#3 og onde#4. Men de er stadig nyttige nogle gange. Skam dig, føl dig skyldig, men når en ond konstruktion som en makro forbedrer din software, så brug den.

Hvordan deklarerer jeg en pointer-to-member-funktion, der peger på en const member-funktion?

Kort svar: Tilføj en const til højre for ), når du bruger en typedef til at deklarere member-funktion-pointertypen.

For eksempel, lad os antage, at du vil have en pointer-to-member-function, der peger på Fred::f, Fred::g eller Fred::h:

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

Når du så bruger en typedef til at deklarere member-function-pointertypen, skal det se sådan ud:

Sådan skal det være!

Så kan du deklarere/passere/returnerer member-funktions-pointere ligesom normalt:

Hvad er forskellen mellem .*- og ->*-operatorerne?

Du behøver ikke at forstå dette, hvis du bruger std::invoke eller en makro til kald af member-funktions-pointere. Ohyea, du bedes bruge std::invoke eller en makro i dette tilfælde. Og nævnte jeg, at du skal bruge std::invoke eller en makro i dette tilfælde??!?

For eksempel:

Men overvej venligst at bruge en std::invoke eller en makro i stedet:

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);}

eller

Som tidligere omtalt er virkelige kald ofte meget mere komplicerede end de simpleoner her, så brugen af en std::invoke eller en makro vil typisk forbedre din kodes skrive- og læsbarhed.

Kan jeg konvertere en pointer-to-member-funktion til en void*?

Nej!

Tekniske detaljer: Pointers til member-funktioner og pointers til data er ikke nødvendigvis repræsenteret på samme måde. En pegepind til en medlemsfunktion kan være en datastruktur i stedet for en enkelt pegepind. Tænk over det: Hvis den peger på en virtuel funktion, peger den måske ikke på en statisk opløselig bunke kode, så det er måske ikke engang en normal adresse – det er måske en anden datastruktur af en eller anden art.

Skynd dig ikke at sende mig en e-mail, hvis ovenstående ser ud til at virke på din bestemte version af din bestemte compiler på dit bestemte operativsystem. Jeg er ligeglad. Det er ulovligt, punktum.

Kan jeg konvertere en pointer-to-function til en void*?

Nej!

Tekniske detaljer: void* pointers er pointers til data, og funktions-pointers peger på funktioner. Sproget kræver ikke, at funktioner og data befinder sig i samme adresseområde, så på arkitekturer, hvor de befinder sig i forskellige adresseområder, vil de to forskellige pointertyper ikke være sammenlignelige.

Du skal ikke sende mig en e-mail, hvis ovenstående ser ud til at virke på din bestemte version af din bestemte compiler på dit bestemte styresystem. Jeg er ligeglad. Det er ulovligt, punktum.

Jeg har brug for noget, der ligner funktionspejle, men med større fleksibilitet og/eller trådsikkerhed; er der en anden måde?

Brug en functionoid.

Hvad pokker er en functionoid, og hvorfor skulle jeg bruge en?

Functionoids er funktioner på steroider. Functionoids er strengt taget mere kraftfulde end funktioner, og denne ekstra kraft løser nogle (ikke alle) af de udfordringer, man typisk står over for, når man bruger funktionsvisere.

Lad os arbejde med et eksempel, der viser en traditionel brug af funktionsvisere, og derefter vil vi oversætte dette eksempel tilofunctionoids. Den traditionelle idé med funktionspointer er, at man har en masse kompatible funktioner:

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

Så tilgår man disse ved hjælp af funktionspointer:

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

I nogle tilfælde opretter folk et array af disse funktionspointer:

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

I så fald kalder de funktionen ved at tilgå arrayet:

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

Med functionoids opretter man først en basisklasse med en rent virtuel metode:

Så opretter man i stedet for tre funktioner tre afledte klasser:

Så opretter man i stedet for at overdrage en funktions-pointer en Funct*. Jeg opretter en typedef kaldet FunctPtr blot for at gøre resten af koden magen til den gammeldags fremgangsmåde:

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

Du kan oprette et array af dem på næsten samme måde:

Dette giver os det første hint om, hvor functionoids er strengt taget mere kraftfulde end function-pointers: det faktum, at den functionoid-tilgang har argumenter, som du kan sende til ctorerne (vist ovenfor som …ctor-args…), hvorimod function-pointers-versionen ikke har det. Tænk på et functionoid-objekt som et frysetørret funktionskald (med vægt på ordet kald). I modsætning til en pointer til en funktion er en functionoid (begrebsmæssigt) en pointer til en delvist kaldt funktion. Forestil dig for et øjeblik en teknologi, der gør det muligt at sende nogle, men ikke alle argumenter til en funktion, og som derefter lader dig frysetørre dette (delvist gennemførte) kald. Forestil dig, at denne teknologi giver dig en slags magisk pegepind tilbage til det frysetørrede delvist afsluttede funktionsopkald. Senere overfører du så de resterende args ved hjælp af denne pointer, og systemet tager på magisk vis dine oprindelige args (som blev frysetørret), kombinerer dem med eventuelle lokale variabler, som funktionen beregnede, før den blev frysetørret, kombinerer alt dette med de nyligt overførte args og fortsætter funktionens udførelse, hvor den slap, da den blev frysetørret. Det lyder måske som science fiction, men det er i princippet det, man kan gøre med functionoids. Desuden lader de dig gentagne gange “fuldføre” den frysetørrede funktionskalle med forskellige “resterende parametre”, så ofte du vil. Plus de tillader (ikke kræver), at du ændrer den frysetørrede tilstand, når den bliver kaldt, hvilket betyder, at functionoids kan huske oplysninger fra et kald til det næste.

Lad os få fødderne tilbage på jorden, og vi vil arbejde et par eksempler for at forklare, hvad alt dette mumbo jumbo virkelig betyder.

Sæt, at de oprindelige funktioner (i den gammeldags funktion-pointer-stil) tog lidt forskellige parametre.

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...*/ }

Når parametrene er forskellige, er det svært at bruge den gammeldags funktions-pointer-metode, da den kaldende part ikke ved, hvilke parametre der skal overleveres (den kaldende part har blot en pointer til funktionen, ikke funktionens navn eller,når parametrene er forskellige, antallet og typerne af dens parametre) (skriv ikke en e-mail til mig om dette; ja, du kan gøre det, men du skal stå på hovedet og lave rodede ting; men skriv ikke til mig om det – brug i stedet funktionsoider).

Med functionoids er situationen, i hvert fald nogle gange, meget bedre. Da en functionoid kan opfattes som et frysetørret funktionskald, skal du blot tage de ikke-almindelige args, som dem jeg har kaldt y og/eller z, og lave dem til margs til de tilsvarende ctors. Du kan også sende de almindelige args (i dette tilfælde int kaldet x) til ctor’en, men det behøver du ikke – du har mulighed for at sende dem til den rent virtuelle doit()-metode i stedet. Jeg antager, at du ønsker at videregive x til doit() og y og/eller z til ctorerne:

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

Så opretter du i stedet for tre funktioner tre afledte klasser:

Nu kan du se, at ctorens parametre bliver frysetørret ind i funktionsoidet, når du opretter arrayet af funktionsoider:

Så når brugeren påkalder doit() på en af disse functionoids, leverer han de “resterende” args, og kalderkonceptet kombinerer de oprindelige args, der er givet til ctor’en, med dem, der er givet til doit()-metoden:

array->doit(12);

Som jeg allerede har antydet, er en af fordelene ved funktionoider, at du kan have flere instanser af f.eks. Funct1 i dit array, og disse instanser kan have forskellige parametre, der fryses ned i dem. F.eks. er array ogarray begge af typen Funct1, men array->doit(12)s opførsel vil være forskellig fra array->doit(12)s opførsel, da opførslen vil afhænge af både den 12, der blev overgivet til doit(), og de args, der blev overgivet til ctors.

En anden fordel ved functionoids er tydelig, hvis vi ændrer eksemplet fra et array af functionoids til en localfunctionoid. For at sætte scenen, lad os gå tilbage til den gammeldags funktions-pointer-tilgang og forestille os, at du forsøger at videregive en sammenligningsfunktion til en sort() eller binarySearch() rutine. sort()– eller binarySearch()-routinen hedder childRoutine() og sammenligningsfunktions-pointertypen hedder FunctPtr:

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

Så ville forskellige callers videregive forskellige funktions-pointere afhængigt af, hvad de mente var bedst:

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

Vi kan nemt oversætte dette eksempel til et eksempel med functionoids:

Med dette eksempel som baggrund kan vi se to fordele ved functionoids frem for function-pointers. Den “ctor args”-fordel, der er beskrevet ovenfor, plus det faktum, at functionoids kan opretholde tilstand mellem kald på en trådsikker måde. med almindelige funktionspointer opretholder folk normalt tilstand mellem kald via statiske data. Statiske data er imidlertid ikke uadskilleligt trådsikre – statiske data deles mellem alle tråde. Med funktionoid-tilgangen får man noget, der i sig selv er trådsikkert, da koden ender med trådlokale data. Implementeringen er simpel: ændre det gammeldags statiske datum til et instansdatamedlem inden for funktionoidens this-objekt, og poof, dataene er ikke kun trådlokale, men de er endda sikre ved rekursive kald: hvert kald til yourCaller() vil have sit eget Funct3-objekt med sine egne instansdata.

Bemærk, at vi har vundet noget uden at miste noget. Hvis du ønsker trådglobale data, kan functionoids også give youthat: du skal blot ændre det fra et instansdatamedlem inden for functionoidens this-objekt til et statisk datamedlem inden for functionoidens klasse, eller endda til et statisk data med lokalt anvendelsesområde. Du ville ikke være bedre stillet end medfunction-pointere, men du ville heller ikke være dårligere stillet.

Funktionoid-tilgangen giver dig en tredje mulighed, som ikke er tilgængelig med den gammeldags tilgang: funktionoidet lader kalderne bestemme, om de vil have tråd-lokale eller tråd-globale data. De ville være ansvarlige for at brugeelocks i de tilfælde, hvor de ønskede tråd-globale data, men i det mindste ville de have valget. Det er let:

Functionoids løser ikke alle problemer, som man støder på, når man laver fleksibel software, men de er klart mere kraftfulde end funktionspointer, og de er i det mindste værd at evaluere. Faktisk kan man let bevise, at functionoids ikke mister nogen styrke i forhold til function-pointers, da man kan forestille sig, at den gammeldags fremgangsmåde med function-pointers svarer til at have et globalt(!) functionoid-objekt. Da man altid kan lave et globalt funktionoid-objekt, har man ikke mistet noget. QED.

Kan man lave functionoids hurtigere end normale funktionskald?

Ja.

Hvis man har et lille functionoid, og i den virkelige verden er det ret almindeligt, kan omkostningerne ved funktionskaldet være høje i forhold til omkostningerne ved det arbejde, der udføres af functionoidet. I den tidligere FAQ blev funktionoider implementeret ved hjælp af virtuelle funktioner og vil typisk koste dig et funktionskald. En alternativ fremgangsmåde er brug af skabeloner.

Det følgende eksempel ligner i ånden det i den tidligere FAQ. Jeg har omdøbt doit() til operator()() for at forbedre læsbarheden af koden for opkalderen og for at give nogen mulighed for at overdrage en almindelig funktions-pointer:

Skellen mellem denne fremgangsmåde og den i den tidligere FAQ er, at fuctionoidet bliver “bundet” til opkalderen på kompileringstidspunktet i stedet for på kørselstidspunktet. Tænk på det som at indlevere en parameter: Hvis du på kompileringstidspunktet ved, hvilken type funktionoid du i sidste ende vil indlevere, kan du bruge ovenstående teknik, og du kan, i det mindste i typiske tilfælde, få en hastighedsfordel ved at få compileren til at udvide funktionoidkoden i kalderens linje. Her er et eksempel:

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

Når compileren kompilerer ovenstående, vil den måske inline-expandere opkaldet, hvilket kan forbedre ydeevnen.

Her er en måde at kalde ovenstående på:

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

Bortset fra det: Som det blev antydet i første afsnit ovenfor, kan du også overgive navnene på normale funktioner (selvom du måske pådrager dig omkostningerne ved funktionskaldet, når kalderen bruger disse):

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

Hvad er forskellen mellem en functionoid og en functor?

En functionoid er et objekt, der har én vigtig metode. Det er dybest set en OO-udvidelse af en C-lignende funktion som f.eks. asprintf(). Man bruger en functionoid, når funktionen har mere end ét indgangspunkt (dvs. mere end én “metode”) og/eller skal opretholde status mellem kald på en trådsikker måde (den C-stilistiske metode til at opretholde status mellem kald er at tilføje en lokal “statisk” variabel til funktionen, men det er frygteligt usikkert i et miljø med flere tråde).

En functor er et særligt tilfælde af en functionoid: det er en functionoid, hvis metode er “funktionskaldsoperatoren”, operator()(). Da den overbelaster funktionsopkaldsoperatoren, kan kode kalde dens hovedmetode ved hjælp af den samme syntaks, som den ville gøre for et funktionskald. Hvis f.eks. “foo” er en functor, vil man for at kalde “operator()()”-metoden på “foo”-objektet sige “foo()”. Fordelen ved dette er i skabeloner, da skabelonen så kan have en skabelonparameter, der vil blive brugt som en funktion, og denne parameter kan enten være navnet på en funktion eller et functor-objekt. Der er en ydelsesmæssig fordel ved at det er et functor-objekt, da “operator()()”-metoden kan være inlinet (hvorimod hvis du overdrager adressen på en funktion, skal den nødvendigvis være ikke-inlinet).

Dette er meget nyttigt for ting som “comparison”-funktionen på sorterede containere. I C overgives sammenligningsfunktionen altid som en pointer (f.eks, se signaturen til “qsort()”), men i C++ kan parameteren komme ind enten som en pegepind til en funktion ELLER som navnet på et funktor-objekt, og resultatet er, at sorterede containere i C++ i nogle tilfælde kan være meget hurtigere (og aldrig langsommere) end det tilsvarende i C.

Da Java ikke har noget, der ligner skabeloner, må det bruge dynamisk binding til alle disse ting, og dynamisk binding betyder nødvendigvis et funktionskald. Normalt er det ikke noget stort problem, men i C++ ønsker vi at muliggøre ekstremt højtydende kode. Det vil sige, at C++ har en “betal kun for det, hvis du bruger det”-filosofi, hvilket betyder, at sproget aldrig vilkårligt må pålægge nogen form for overhead i forhold til, hvad den fysiske maskine er i stand til at udføre (selvfølgelig kan en programmør eventuelt anvende teknikker som dynamisk binding, der generelt vil pålægge et vist overhead til gengæld for fleksibilitet eller en anden “ility”, men det er op til designeren og programmøren at beslutte, om de vil have fordelene (og omkostningerne) ved sådanne konstruktioner).

Leave a Reply