skabeloner i C++Rediger
c++ bruger skabeloner til at aktivere generiske programmeringsteknikker. C++ standardbiblioteket indeholder Standardskabelonbiblioteket eller STL, der giver en ramme af skabeloner til fælles datastrukturer og algoritmer. Skabeloner i C++ kan også bruges til skabelon metaprogrammering, som er en måde at pre-evaluere nogle af koden på kompileringstid snarere end run-time. Ved hjælp af skabelonspecialisering betragtes C++ skabeloner som Turing komplet.,
teknisk oversigtdit
Der er to slags skabeloner: funktionsskabeloner og klasseskabeloner. En funktion skabelon er et mønster for at skabe almindelige funktioner baseret på parametrisering typer leveres, når instantiated. For eksempel indeholder C++ Standard Skabelonbiblioteket funktionsskabelonen ma. (,, y), der opretter funktioner, der returnerer enten. eller y, alt efter hvad der er større., max()
kunne defineres som dette:
template <typename T>T max(T x, T y) { return x < y ? y : x;}
Specialiseringer af denne funktion skabelon, instantiations med bestemte typer, der kan kaldes bare som en almindelig funktion:
std::cout << max(3, 7); // Outputs 7.
compileren undersøger argumenter, der bruges til at ringe antal og afgør, at dette er et kald til max(int, int)., Derefter starter en version af den funktion, hvor den parameterizing type T er int, hvilket svarer til følgende funktion:
int max(int x, int y) { return x < y ? y : x;}
Dette virker, om de argumenter, x og y er hele tal, strenge, eller enhver anden form for hvilke udtryk, x < y er fornuftigt, eller mere specifikt, for nogen form for hvilken operatør< er defineret. Fælles arv er ikke nødvendig for det sæt af typer, der kan bruges, og det ligner derfor meget duck typing., Et program, der definerer en brugerdefineret datatype, kan bruge operatøroverbelastning til at definere betydningen af < for den type, hvilket tillader dets anvendelse med MA. () – funktionsskabelonen. Selvom dette kan virke som en mindre fordel i dette isolerede eksempel, giver det i forbindelse med et omfattende bibliotek som STL det programmereren mulighed for at få omfattende funktionalitet til en ny datatype, bare ved at definere et par operatører til det., Blot definere< tillader en type, der skal bruges med standard sortering (), stable_sort (), og binary_search() algoritmer eller sættes i datastrukturer såsom sæt, dynger og associative arrays.
c++ skabeloner er helt skrive sikker på kompileringstidspunktet. Som en demonstration definerer standardtypekomplekset ikke< operatør, fordi der ikke er nogen streng ordre på komplekse tal. Derfor vil ma. (,, y) mislykkes med en kompileringsfejl, hvis. og y er komplekse værdier., Ligeledes kan andre skabeloner, der er afhængige af <, ikke anvendes på komplekse data, medmindre der findes en sammenligning (i form af en funktion eller funktion). F. eks.: et kompleks kan ikke bruges som nøgle til et kort, medmindre der gives en sammenligning. Desværre genererer kompilatorer Historisk noget esoteriske, lange og uhensigtsmæssige fejlmeddelelser til denne slags fejl. At sikre, at et bestemt objekt overholder en metodeprotokol, kan afhjælpe dette problem. Sprog, der bruger sammenligne i stedet for < kan også bruge komplekse værdier som nøgler.,
den anden slags skabelon, en klasseskabelon, udvider det samme koncept til klasser. En klasse skabelon specialisering er en klasse. Klasse skabeloner bruges ofte til at lave generiske containere. For eksempel har STL en linket listebeholder. For at lave en linket liste over heltal skriver man liste<int>. En liste over strenge betegnes liste<string>. En liste har et sæt standardfunktioner forbundet med det, der fungerer for alle kompatible parameteriseringstyper.,
Template speciali .ationedit
en kraftfuld funktion af C++’s skabeloner er template specialisering. Dette gør det muligt at tilvejebringe alternative implementeringer baseret på visse egenskaber ved den parametriserede type, der instantieres. Skabelon specialisering har to formål: at tillade visse former for optimering, og for at reducere kode bloat.
for eksempel overveje en slags() skabelon funktion. En af de primære aktiviteter, som en sådan funktion gør, er at bytte eller udveksle værdierne i to af containerens positioner., Hvis værdierne er store (med hensyn til antallet af bytes, der kræves for at gemme hver af dem), er det ofte hurtigere at først opbygge en separat liste over pointers til objekterne, sortere disse pointers og derefter opbygge den endelige sorterede sekvens. Hvis værdierne er ganske små, men det er normalt hurtigst at bare bytte værdierne på plads efter behov. Desuden, hvis den parametriserede type allerede er af en vis pointer-type, er der ikke behov for at opbygge et separat pointerarray., Skabelonspecialisering giver skabelonskaberen mulighed for at skrive forskellige implementeringer og specificere de egenskaber, som den parametriserede type(R) skal have for hver implementering, der skal bruges.
i modsætning til funktionsskabeloner kan klasseskabeloner være delvist specialiserede. Det betyder, at en alternativ version af klasseskabelonkoden kan leveres, når nogle af skabelonparametrene er kendt, mens andre skabelonparametre er generiske., Bruges til at oprette en standardimplementering (den primære specialisering), der antager, at kopiering af en parameteriseringstype er dyr og derefter oprette delvise specialiseringer for typer, der er billige at kopiere, hvilket øger den samlede effektivitet. Kunder af en sådan klasseskabelon bruger bare specialiseringer af det uden at skulle vide, om kompilatoren brugte den primære specialisering eller en delvis specialisering i hvert enkelt tilfælde., Klasseskabeloner kan også være fuldt specialiserede, hvilket betyder, at en alternativ implementering kan leveres, når alle parameteriseringstyper er kendt.
fordele og ulemperedit
Nogle Anvendelser af skabeloner, såsom Ma. ()-funktionen, blev tidligere udfyldt af funktionslignende præprocessormakroer (en arv fra C-programmeringssproget). For eksempel er her en mulig maks() Makro:
#define max(a,b) ((a) < (b) ? (b) : (a))
makroer udvides med præprocessor, før kompilering korrekt; skabeloner udvides på kompileringstidspunktet., Makroer udvides altid inline; skabeloner kan også udvides som inline-funktioner, når kompilatoren finder det passende. Således både funktionslignende makroer og funktion skabeloner har ingen run-time overhead.
skabeloner betragtes dog generelt som en forbedring i forhold til makroer til disse formål. Skabeloner er type-safe. Skabeloner undgår nogle af de almindelige fejl, der findes i kode, der gør stor brug af funktionslignende makroer, såsom evaluering af parametre med bivirkninger to gange. Måske vigtigst af alt var skabeloner designet til at kunne anvendes til meget større problemer end makroer.,
Der er fire primære ulemper ved at bruge skabeloner: understøttede funktioner, compiler støtte, dårlige fejlmeddelelser og kode bloat:
- Templates i C++ mangler mange features, som gør at gennemføre dem, og bruge dem på en enkel måde, ofte umuligt. I stedet programmører nødt til at stole på komplicerede tricks, som fører til oppustet, svært at forstå og svært at vedligeholde kode. Den aktuelle udvikling i C++ – standarderne forværrer dette problem ved at gøre stor brug af disse tricks og opbygge en masse nye funktioner til skabeloner på dem eller med dem i tankerne.,mange compilere har historisk set dårlig understøttelse af skabeloner, og brugen af skabeloner kan således gøre kode noget mindre bærbar. Support kan også være dårlig, når en C++-compiler bruges sammen med en linker, der ikke er C++ – opmærksom, eller når man forsøger at bruge skabeloner på tværs af delte biblioteksgrænser. De fleste moderne compilere har dog nu ret robust og standard skabelon support, og den nye C++ standard, C++11, løser yderligere disse problemer.
- næsten alle compilere producerer forvirrende, lange eller undertiden uhensigtsmæssige fejlmeddelelser, når der opdages fejl i kode, der bruger skabeloner., Dette kan gøre skabeloner vanskelige at udvikle.
- endelig kræver brug af skabeloner, at kompilatoren genererer en separat forekomst af den templerede klasse eller funktion for hver permutation af typeparametre, der bruges sammen med den. (Dette er nødvendigt, fordi typer i C++ ikke er alle i samme størrelse, og størrelserne på datafelter er vigtige for, hvordan klasser fungerer.) Så den vilkårlige brug af skabeloner kan føre til kodeopblussen, hvilket resulterer i for store eksekverbare filer., Men en velovervejet brug af skabelon specialisering og udledning dramatisk kan reducere en sådan kode bloat i nogle tilfælde:
Så, kan udledningen bruges til at reducere problemet med koden gentages, fordi skabeloner bruges? Dette ville indebære at udlede en skabelon fra en almindelig klasse. Denne teknik viste sig at være vellykket med at begrænse kodeopblussen i reel brug. Folk, der ikke bruger en teknik som denne, har fundet ud af, at replikeret kode kan koste megabyte kodeplads, selv i programmer med moderat størrelse.,
— Bjarne Stroustrup, The Design and Evolution af C++, 1994
Den ekstra instantiations genereret af skabeloner kan også forårsage debuggers at have svært ved at arbejde sammen med skabeloner. For eksempel, indstilling af en fejlfinding breakpoint i en skabelon fra en kildefil kan enten gå glip af indstilling af breakpoint i den faktiske instantiering ønskede eller kan sætte et breakpoint i hvert sted skabelonen instantieres.,da kompilatoren skal udføre makrolignende udvidelser af skabeloner og generere forskellige forekomster af dem på kompileringstidspunktet, skal implementeringskildekoden for den templerede klasse eller funktion være tilgængelig (f.eks. inkluderet i en overskrift) til koden ved hjælp af den. Templated klasser eller funktioner, herunder meget af Standard Template Library( STL), hvis det ikke er inkluderet i headerfiler, kan ikke kompileres. (Dette er i modsætning til ikke-templated kode, som kan kompileres til binær, giver kun en erklæringer header fil for kode ved hjælp af det.,) Dette kan være en ulempe ved at afsløre implementeringskoden, som fjerner nogle abstraktioner og kan begrænse brugen i projekter med lukket kilde.
skabeloner i DEdit
d programmeringssprog understøtter skabeloner baseret i design på C++.,De fleste C++ skabelon udtryk vil bære over D uden ændringer, men D tilføjer nogle ekstra funktioner:
- Template parametrene i D er ikke begrænset til primitive typer og værdier, men også tillade kørsel af vilkårlig compile-time værdier (såsom strenge og struct konstanter), og aliaser til vilkårlig identifikatorer, herunder andre skabeloner eller skabelon instantiations.
- Skabelonbegrænsninger, og den statiske if-sætning giver et alternativ til C++’s substitutionsfejl er ikke en fejl (SFINAE) – mekanisme, der ligner C++ – koncepter.
- den er (…,) udtryk tillader spekulativ instantiering at verificere et objekts træk på kompileringstidspunktet.
- det automatiske søgeord og typeof-udtrykket tillader type inferens for variable erklæringer og funktion returværdier, som igen tillader “Voldemort-typer” (typer, der ikke har et globalt navn).
Skabeloner i D-bruge en anden syntaks end i C++: henviser til, at der i C++ skabelon parametre, der er pakket ind i kantede parenteser (Skabelon<param1, param2>),D bruger et udråbstegn og parenteser: Skabelon!(param1, param2).,Dette undgår C++ parsing vanskeligheder på grund af tvetydighed med sammenligning operatører.Hvis der kun er en parameter, kan parenteserne udelades.
konventionelt kombinerer D ovenstående funktioner for at tilvejebringe kompileringstidspolymorfisme ved hjælp af trækbaseret generisk programmering.,For eksempel, en input-området er defineret som en type, der opfylder den kontrol, der udføres af isInputRange, som er defineret som følger:
En funktion, der accepterer kun input af områder kan så bruge ovenstående skabelon i skabelon constraint:
auto fun(Range)(Range range) if (isInputRange!Range){ // ...}
– Kode generationEdit
ud over template metaprogramming, D også indeholder flere funktioner til at aktivere compile-time code generation:
- import udtryk giver mulighed for at læse en fil fra disk, og ved hjælp af dens indhold som en streng udtryk.,
- Compile-time refleksion tillader optælling og inspektion erklæringer og deres medlemmer under udarbejdelse.
- brugerdefinerede attributter giver brugerne mulighed for at vedhæfte vilkårlige identifikatorer til erklæringer, som derefter kan opregnes ved hjælp af kompileringstidsreflektion.
- Compile-Time Function e .ecution (CTFE) gør det muligt at fortolke en delmængde af D (begrænset til sikre operationer) under kompilering.
- String mi .ins tillader evaluering og kompilering af indholdet af en streng udtryk som D-kode, der bliver en del af programmet.,
Ved at kombinere ovenstående kan generere kode baseret på eksisterende erklæringer.For eksempel, D serialisering rammer kan opregne en type medlemmer og generere specialiserede funktioner for hver føljeton typeto udføre serialisering og deserialisering.Brugerdefinerede attributter kan yderligere indikere serialiseringsregler.
import udtryk og kompilering-tid funktion udførelse tillader også effektivt at gennemføre domænespecifikke sprog.,For eksempel, da en funktion, der tager en string, der indeholder en HTML-skabelon og afkast svarende D kildekode, er det muligt at bruge det på følgende måde:
Genericity i EiffelEdit
Generiske klasser har været en del af Eiffel, da den oprindelige metode og sprog design. Eiffels grundpublikationer bruger udtrykket genericitet til at beskrive oprettelsen og brugen af generiske klasser.
Basic/Unconstrained genericityEdit
generiske klasser erklæres med deres klassenavn og en liste over en eller flere formelle generiske parametre., I den følgende kode, klasse LIST
har en formel generiske parameter G
Den formelle generiske parametre er pladsholdere for vilkårlig klasse navne, der vil blive leveret, når en erklæring om, at den generiske klasse er lavet, som vist i de to generiske udgaver nedenfor, hvor ACCOUNT
og DEPOSIT
er andre klasse navne. ACCOUNT
ogDEPOSIT
betragtes som faktiske generiske parametre, da de giver reelle klassenavne til erstatning forG
I faktisk brug.,
list_of_accounts: LIST -- Account list list_of_deposits: LIST -- Deposit list
inden for Eiffel-typen, selvom klasse LIST
betragtes som en klasse, betragtes det ikke som en type. Imidlertid betragtes en generisk afledning af LIST
såsom LIST
som en type.
begrænset genericitydit
for listeklassen vist ovenfor kan en faktisk generisk parameter, der erstatterG
, være enhver anden tilgængelig klasse., For at begrænse det sæt klasser, hvorfra gyldige faktiske generiske parametre kan vælges, kan en generisk begrænsning specificeres. I erklæringen om klasse SORTED_LIST
nedenfor dikterer den generiske begrænsning, at enhver gyldig faktisk generisk parameter vil være en klasse, der arver fra klasse COMPARABLE
. Den generiske begrænsning sikrer, at elementer af en SORTED_LIST
faktisk kan sorteres.,
class SORTED_LIST
Generiske lægemidler i JavaEdit
Støtte til generiske lægemidler, eller “containere-af-type-T” blev tilføjet til programmeringssproget Java i 2004 som en del af J2SE 5.0. I Java kontrolleres generika kun på kompileringstidspunktet for typenes korrekthed. Den generiske type information fjernes derefter via en proces kaldet type sletning for at opretholde kompatibilitet med gamle JVM-implementeringer, hvilket gør den utilgængelig ved kørsel., For eksempel konverteres en liste<String> til listen over ra. – typer. Compiler indsætter typen kaster til at konvertere elementerne til strengen type, når de hentes fra listen, hvilket reducerer ydeevnen i forhold til andre implementeringer såsom C++ skabeloner.
Genericity i .NET Rediger
Generiske lægemidler blev tilføjet som en del af .NET Framework 2.0 i November 2005, der er baseret på et forsknings-prototype fra Microsoft Research startede i 1999. Selvom ligner generika i Java, .,NET generika anvender ikke typesletning, men implementerer generika som en førsteklasses mekanisme i runtime ved hjælp af reification. Dette designvalg giver yderligere funktionalitet, såsom at tillade refleksion med bevarelse af generiske typer, samt lindre nogle af begrænsningerne i sletning (såsom at være ude af stand til at oprette generiske arrays). Det betyder også, at der ikke er nogen ydeevne hit fra runtime kaster og normalt dyre boksning konverteringer., Når primitive og værdityper bruges som generiske argumenter, får de specialiserede implementeringer, der giver mulighed for effektive generiske samlinger og metoder. Som i C++ og Java, indlejrede generiske typer såsom Ordbog<string, List<int>> er gyldig typer, dog er frarådede medlem underskrifter i koden analyse, design regler.
.,NET tillader seks sorter af generiske typebegrænsninger ved hjælp af keyhere-nøgleordet, herunder begrænsning af generiske typer, at være værdityper, at være klasser, at have konstruktører og at implementere grænseflader. Nedenfor er et eksempel med en grænseflade med en constraint:
MakeAtLeast () – metoden kan bruges på arrays, med elementer af generiske type T. metoden type begrænsning indikerer, at metoden er anvendelig til enhver type T, der implementerer den generiske IComparable<T> brugerflade., Dette sikrer en kompileringstidsfejl, hvis metoden kaldes, hvis typen ikke understøtter sammenligning. Interfacet giver den generiske metode CompareTo (t).
ovenstående metode kan også skrives uden generiske typer, blot ved hjælp af den ikke-generiske Arraytype. Da arrays imidlertid er kontravariante, ville støbningen ikke være type sikker, og kompilatoren ville ikke være i stand til at finde visse mulige fejl, der ellers ville blive fanget, når du bruger generiske typer. Derudover skal metoden i stedet få adgang til arrayelementerne som objekter og kræve støbning for at sammenligne to elementer., (For værdien typer som typer som int dette kræver en boxing konvertering, selvom det kan være arbejdet omkring hjælp Comparer<T> class, som er gjort i standard samling klasser.)
en bemærkelsesværdig opførsel af statiske medlemmer i en generisk.NET-klasse er statisk elementinstantiering pr.
Genericitet i DelphiEdit
Delphi ‘ s Object Pascal dialect erhvervede generics i Delphi 2007-udgivelsen, oprindeligt kun med (nu afbrudt) .,NET compiler, før de føjes til den native kode i Delphi 2009 udgivelse. Semantikken og mulighederne i Delphi generics er stort set modelleret efter dem, der var af generics i.Net 2.0, selvom implementeringen nødvendigvis er helt anderledes. Her er en mere eller mindre direkte oversættelse af det første C# eksempel vist ovenfor:
som med C# kan metoder såvel som hele typer have en eller flere typeparametre. I eksemplet, TArray er en generisk type (defineret af sproget) og MakeAtLeast en generisk metode., De tilgængelige begrænsninger ligner meget de tilgængelige begrænsninger i C#: enhver værditype, enhver klasse, en bestemt klasse eller grænseflade og en klasse med en parameterløs konstruktør. Flere begrænsninger fungerer som en additiv union.
Genericitet i Free PascalEdit
Free Pascal implementerede generika før Delphi og med forskellige syntaks og semantik. Men siden FPC version 2.6.0 er Delphi – stil syntaks tilgængelig, når du bruger {$mode Delphi} sprogtilstand. Således Er Gratis Pascal-programmører i stand til at bruge generika i hvilken stil de foretrækker.,
Delphi og Free Pascal eksempel:
funktionelle sprogRediger
Genericitet i HaskellEdit
typeklassemekanismen for Haskell understøtter generisk programmering.Seks af de foruddefinerede typeklasser i Haskell (inklusive e., de typer, der kan sammenlignes for ligestilling, og Sho., de typer, hvis værdier kan gengives som strenge) har den særlige egenskab at understøtte afledte forekomster., Dette betyder, at en programmør, der definerer en ny type, kan angive, at denne type skal være en forekomst af en af disse specielle typeklasser, uden at give implementeringer af klassemetoderne, som det normalt er nødvendigt, når man erklærer klasseinstanser. Alle de nødvendige metoder vil blive” afledt ” – det vil sige konstrueret automatisk – baseret på strukturen af typen.,For eksempel, følgende erklæring af en type af binære træer, at det er for at være en instans af klasser Eq og Vis:
data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show)
Dette resulterer i en ligestilling funktion (==) og en string repræsentation funktion (vis) bliver automatisk defineret for enhver type af den form BinTree T, forudsat at T selv understøtter disse aktiviteter.,
støtte for afledte tilfælde af Eq og Vis gør deres metoder, = = og vis generisk på en kvalitativt anderledes måde fra para-metrisk polymorfe funktioner: disse “funktioner” (mere præcist, type-indekseret familier af funktioner), der kan anvendes til værdier, der er af forskellige typer, og selv om de opfører sig forskelligt for hver type argument, lidt arbejde, der er nødvendigt for at understøtte en ny type. Ralf Hin .e (2004) har vist, at en lignende effekt kan opnås for brugerdefinerede typeklasser ved hjælp af visse programmeringsteknikker., Andre forskere har foreslået tilgange til denne og andre former for genericitet i forbindelse med Haskell og udvidelser til Haskell (diskuteret nedenfor).
PolyPEdit
PolyP var den første generiske programmeringssprogudvidelse til Haskell. I PolyP kaldes generiske funktioner polytypiske. Sproget introducerer en særlig konstruktion, hvor sådanne polytypiske funktioner kan defineres via strukturel induktion over strukturen af mønstret functor af en regelmæssig datatype. Regelmæssige datatyper i PolyP er en delmængde af Haskell datatyper., En regelmæssig datatype t skal være af type * → *, og hvis a er den formelle type argument i den definition, så alle rekursive kald til t skal have form af t en. Disse begrænsninger udelukke højere kinded datatyper samt indlejrede datatyper, hvor de rekursive kald er af en anden form.Flatten-funktionen i PolyP er her angivet som et eksempel:
generisk HaskellEdit
generisk Haskell er en anden udvidelse til Haskell, udviklet på Utrecht Universitet i Holland., Udvidelserne den giver er:
- Typeindekserede værdier defineres som en værdi indekseret over de forskellige Haskell type konstruktører (enhed, primitive typer, Summer, produkter og brugerdefinerede type konstruktører). Derudover kan vi også specificere adfærd for en type-indekseret værdier for en bestemt konstruktør ved hjælp af konstruktørcases, og genbruge en generisk definition i en anden ved hjælp af standardcases.
den resulterende typeindekserede værdi kan specialiseres til enhver type.
- Kind-indekserede typer er typer indekseret over slags, defineret ved at give en sag for både * og k and k’., Forekomster opnås ved at anvende den slags indekserede type til en slags.
- generiske definitioner kan bruges ved at anvende dem på en type eller art. Dette kaldes generisk ansøgning. Resultatet er en type eller værdi, afhængigt af hvilken slags generisk definition der anvendes.generisk abstraktion gør det muligt at definere generiske definitioner ved at abstrahere en typeparameter (af en given art).
- Type-indekserede typer er typer, der er indekseret over typen konstruktører. Disse kan bruges til at give typer til mere involverede generiske værdier., De resulterende typeindekserede typer kan specialiseres til enhver type.
Som eksempel, ligestilling funktion i Generisk Haskell:
CleanEdit
Clean tilbyder generisk programmering baseret PolyP og generiske Haskell som understøttes af GHC>=6.0. Det parametrizeses af art som dem, men tilbyder overbelastning.
andre sprogRediger
ML-familien af programmeringssprog understøtter generisk programmering gennem parametrisk polymorfisme og generiske moduler kaldet functors.,Både Standard ML og OCaml leverer functors, som ligner klasseskabeloner og Adas generiske pakker. Scheme syntaktiske abstraktioner har også en forbindelse til genericitet – disse er faktisk et supersæt af templating la la c++.
et Verilog-modul kan tage en eller flere parametre, hvortil deres faktiske værdier er tildelt ved instantiering af modulet. Et eksempel er en generisk register array, hvor array bredde er givet via en parameter., En sådan array, kombineret med en generisk wireire vektor, kan gøre en generisk buffer eller hukommelsesmodul med en vilkårlig bitbredde ud af et enkelt modul implementering.
VHDL, der stammer fra Ada, har også generiske evner.
Skriv et svar