mallar i C++Edit
c++ använder mallar för att aktivera generiska programmeringstekniker. C++ standardbiblioteket innehåller Standardmallbiblioteket eller STL som ger en ram av mallar för vanliga datastrukturer och algoritmer. Mallar i C++ kan också användas för mallmetaprogramming, vilket är ett sätt att i förväg utvärdera en del av koden vid kompilering snarare än run-time. Med hjälp av Mall specialisering, C++ mallar anses Turing komplett.,
Technical overviewEdit
det finns två typer av mallar: funktionsmallar och klassmallar. En funktionsmall är ett mönster för att skapa vanliga funktioner baserat på parametriseringstyperna som levereras när de instansieras. Till exempel innehåller C++ Standardmallbiblioteket funktionsmallen max(x, y) som skapar funktioner som returnerar antingen x eller y, beroende på vilket som är större., max()
kan definieras så här:
template <typename T>T max(T x, T y) { return x < y ? y : x;}
specialiseringar av denna funktionsmall, instantiationer med specifika typer, kan kallas precis som en vanlig funktion:
std::cout << max(3, 7); // Outputs 7.
kompilatorn undersöker de argument som används för att anropa max och bestämmer att detta är ett anrop till max(int, int)., Det instansierar sedan en version av funktionen där parametriseringstypen T är int, vilket gör motsvarande följande funktion:
int max(int x, int y) { return x < y ? y : x;}
detta fungerar om argumenten x och y är heltal, strängar eller någon annan typ för vilken uttrycket x < y är förnuftigt, eller mer specifikt, för vilken typ för vilken operatör< < för den typen, vilket tillåter dess användning med max() funktionsmallen. Även om detta kan tyckas vara en mindre fördel i detta isolerade exempel, i samband med ett omfattande bibliotek som STL tillåter programmeraren att få omfattande funktionalitet för en ny datatyp, bara genom att definiera några operatörer för den., Endast definiera< tillåter en typ som ska användas med standard sortering(), stable_sort(), och binary_search() algoritmer eller placeras inuti datastrukturer såsom uppsättningar, högar och associativa matriser.
c++ mallar är helt typ säker vid kompilering tid. Som en demonstration definierar standardtypskomplexet inte< – operatören, eftersom det inte finns någon strikt ordning på komplexa tal. Därför misslyckas max(x, y) med ett kompileringsfel, om x och y är komplexa värden., På samma sätt kan andra mallar som är beroende av < inte tillämpas på komplexa data om inte en jämförelse (i form av en functor eller funktion) tillhandahålls. T. ex.: ett komplex kan inte användas som nyckel för en karta om inte en jämförelse tillhandahålls. Tyvärr genererar kompilatorer historiskt något esoteriska, långa och ohjälpliga felmeddelanden för denna typ av fel. Att säkerställa att ett visst objekt följer ett metodprotokoll kan lindra denna fråga. Språk som använder jämför istället för< kan också använda komplexa värden som nycklar.,
den andra typen av Mall, en klassmall, utökar samma koncept till klasser. En klassmall specialisering är en klass. Klassmallar används ofta för att göra generiska Behållare. Till exempel har stl en länkad listbehållare. För att skapa en länkad lista med heltal skriver man listan<int>. En lista med strängar betecknas lista<string>. En lista har en uppsättning standardfunktioner som är associerade med den, som fungerar för alla kompatibla parametriseringstyper.,
Mall specializationEdit
ett kraftfullt inslag i C++’S mallar är mall specialisering. Detta gör att alternativa implementeringar kan tillhandahållas baserat på vissa egenskaper hos den parametriserade typen som instansieras. Mall specialisering har två syften: att tillåta vissa former av optimering, och att minska kod svälla.
tänk till exempel på en sorteringsmallfunktion. En av de primära aktiviteter som en sådan funktion gör är att byta eller byta ut värdena i två av behållarens positioner., Om värdena är stora (när det gäller antalet byte som krävs för att lagra var och en av dem) är det ofta snabbare att först bygga en separat lista med pekare till objekten, Sortera dessa pekare och sedan bygga den slutliga sorterade sekvensen. Om värdena är ganska små men det är oftast snabbast att bara byta värden på plats efter behov. Dessutom, om den parametriserade typen redan är av någon pekare-typ, är det inte nödvändigt att bygga en separat pekarmatris., Mallspecialisering gör det möjligt för mallskaparen att skriva olika implementeringar och ange de egenskaper som parametriserade typ(er) måste ha för varje implementering som ska användas.
till skillnad från funktionsmallar kan klassmallar vara delvis specialiserade. Det innebär att en alternativ version av klassmallkoden kan tillhandahållas när några av mallparametrarna är kända, samtidigt som andra mallparametrar lämnas generiska., Detta kan till exempel användas för att skapa en standardimplementering (den primära specialiseringen) som förutsätter att kopiering av en parametriseringstyp är dyr och sedan skapa partiella specialiseringar för typer som är billiga att kopiera, vilket ökar den totala effektiviteten. Kunder av en sådan klassmall använder bara specialiseringar av det utan att behöva veta om kompilatorn använde den primära specialiseringen eller någon partiell specialisering i varje enskilt fall., Klassmallar kan också vara helt specialiserade, vilket innebär att en alternativ implementering kan tillhandahållas när alla parametriseringstyper är kända.
fördelar och nackdelarredigera
vissa användningar av mallar, till exempel max ()-funktionen, fylldes tidigare av funktionsliknande preprocessormakron (ett arv från C-programmeringsspråket). Till exempel, här är en möjlig max() Makro:
#define max(a,b) ((a) < (b) ? (b) : (a))
makron utökas av preprocessor, innan sammanställning korrekt; mallar utökas vid kompilering tid., Makron expanderas alltid inline; mallar kan också utökas som inline funktioner när kompilatorn anser det lämpligt. Således har både funktions – liknande makron och funktionsmallar ingen run-time overhead.
mallar anses dock generellt vara en förbättring jämfört med makron för dessa ändamål. Mallar är typsäkra. Mallar undvika några av de vanliga fel som finns i kod som gör tung användning av funktions-liknande makron, till exempel utvärdera parametrar med biverkningar två gånger. Kanske viktigast var mallar utformade för att vara tillämpliga på mycket större problem än makron.,
det finns fyra primära nackdelar med användningen av mallar: funktioner som stöds, kompilatorstöd, dåliga felmeddelanden och kodbloat:
- mallar i C++ saknar många funktioner, vilket gör att implementera dem och använda dem på ett enkelt sätt ofta omöjligt. Istället programmerare måste förlita sig på komplicerade knep som leder till uppsvälld, svårt att förstå och svårt att upprätthålla koden. Den nuvarande utvecklingen i C++ – standarderna förvärrar detta problem genom att använda dessa knep kraftigt och bygga många nya funktioner för mallar på dem eller med dem i åtanke.,
- många kompilatorer har historiskt dåligt stöd för mallar, vilket innebär att användningen av mallar kan göra koden något mindre bärbar. Stödet kan också vara dåligt när en C++ – kompilator används med en linker som inte är C++-medveten, eller när man försöker använda mallar över delade biblioteksgränser. De flesta moderna kompilatorer har dock nu ganska robust och standardmallstöd, och den nya C++ – standarden, C++11, behandlar ytterligare dessa problem.
- nästan alla kompilatorer producerar förvirrande, långa eller ibland ohjälpliga felmeddelanden när fel upptäcks i kod som använder mallar., Detta kan göra mallar svåra att utveckla.
- slutligen kräver användningen av mallar kompilatorn för att generera en separat instans av den mallade klassen eller funktionen för varje permutation av typparametrar som används med den. (Detta är nödvändigt eftersom typer i C++ inte är alla samma storlek, och storleken på datafält är viktiga för hur klasser fungerar.) Så den urskillningslösa användningen av mallar kan leda till kod svälla, vilket resulterar i alltför stora körbara., Dock kan omdömesgill användning av Mall specialisering och härledning dramatiskt minska sådan kod svälla i vissa fall:
Så, kan härledning användas för att minska problemet med kod replikeras eftersom mallar används? Detta skulle innebära att härleda en mall från en vanlig klass. Denna teknik visade sig vara framgångsrik i att begränsa kodblåsa i verklig användning. Människor som inte använder en teknik som denna har funnit att replikerad kod kan kosta megabyte kodutrymme även i måttliga storlek program.,
— Bjarne Stroustrup, Design och utveckling av C++, 1994
de extra instanser som genereras av mallar kan också orsaka felsökare att ha svårt att arbeta graciöst med mallar. Om du till exempel ställer in en felsökningsbrytpunkt i en mall från en källfil kan det hända att du missar att ställa in brytpunkten i den aktuella instansiering som önskas eller kan ställa in en brytpunkt på varje plats som mallen instansieras.,
eftersom kompilatorn måste utföra makroliknande utbyggnader av mallar och generera olika instanser av dem vid kompileringstid måste implementeringskällkoden för den templaterade klassen eller funktionen vara tillgänglig (t.ex. inkluderad i en rubrik) till koden som använder den. Templated klasser eller funktioner, inklusive mycket av Standardmallbiblioteket (STL), om det inte ingår i header-filer, kan inte kompileras. (Detta är i motsats till icke-mallkod, som kan sammanställas till binär, vilket ger endast en deklarations header-fil för kod som använder den.,) Detta kan vara en nackdel genom att exponera implementeringskoden, som tar bort vissa abstraktioner, och kan begränsa användningen i projekt med sluten källkod.
mallar i dedit
programmeringsspråket D stöder mallar baserade på design på C++.,De flesta C++ – Mall idiom kommer att överföras till D utan förändring, men d lägger till några ytterligare funktioner:
- mallparametrar i D är inte begränsade till bara typer och primitiva värden, men tillåter också godtyckliga kompileringstidsvärden (t.ex. strängar och struct-Literaler) och alias till godtyckliga identifierare, inklusive andra mallar eller mallinstanser.
- Mallbegränsningar och den statiska if-satsen ger ett alternativ till C++’s substitutionsfel är inte en felmekanism (sfinae), som liknar C++ – koncept.
- den är(…,) uttryck tillåter spekulativ instantiation för att verifiera ett objekts egenskaper vid sammanställningen.
- Det automatiska sökordet och typenav uttryck tillåter typ inferens för variabeldeklarationer och funktionsreturvärden, vilket i sin tur tillåter ”Voldemort typer” (typer som inte har ett globalt namn).
mallar i D använder en annan syntax än i C++: medan i C++ mallparametrar är insvept i vinkelfästen (Mall<param1, param2>),D använder ett utropstecken och parenteser: Mall!(param1, param2).,Detta undviker C++ parsing svårigheter på grund av tvetydighet med jämförelseoperatörer.Om det bara finns en parameter kan parenteserna utelämnas.
konventionellt kombinerar d ovanstående funktioner för att ge kompileringspolymorfism med hjälp av dragbaserad Generisk programmering.,Till exempel definieras ett inmatningsområde som vilken typ som helst som uppfyller de kontroller som utförs av isInputRange, vilket definieras enligt följande:
en funktion som endast accepterar inmatningsområden kan sedan använda ovanstående Mall i en mallbegränsning:
auto fun(Range)(Range range) if (isInputRange!Range){ // ...}
kodgenerationedit
förutom mallmetaprogramming tillhandahåller D också flera funktioner för att möjliggöra kompilering av tidskodgenerering:
/p>
- importuttrycket gör det möjligt att läsa en fil från disken och använda dess innehåll som Ett stränguttryck.,
- sammanställa-tid reflektion gör uppräkning och inspektion deklarationer och deras medlemmar under sammanställning.
- användardefinierade attribut tillåter användare att bifoga godtyckliga identifierare till deklarationer, som sedan kan räknas upp med hjälp av compile-time reflection.
- Compile-Time Function Execution (CTFE) tillåter att en delmängd av D (begränsad till säkra operationer) tolkas under kompileringen.
- String mixins tillåter att utvärdera och sammanställa innehållet i Ett stränguttryck som d-kod som blir en del av programmet.,
genom att kombinera ovanstående kan generera kod baserat på befintliga deklarationer.Till exempel kan d-serialiseringsramar räkna upp en typs medlemmar och generera specialiserade funktioner för varje serialiserad typatt utföra serialisering och deserialisering.Användardefinierade attribut kan ytterligare indikera serialiseringsregler.
utförandet av funktionen Importera uttryck och kompilera-tid tillåter också att effektivt implementera domänspecifika språk.,Till exempel, med tanke på en funktion som tar en sträng som innehåller en HTML-mall och returnerar motsvarande d-källkod, är det möjligt att använda den på följande sätt:
Genericitet i EiffelEdit
generiska klasser har varit en del av Eiffel sedan den ursprungliga metoden och språkdesignen. Stiftelsen publikationer Eiffel, använda termen genericitet för att beskriva skapandet och användningen av generiska klasser.
Basic/Unconstrained genericityEdit
generiska klasser deklareras med deras klassnamn och en lista över en eller flera formella generiska parametrar., I följande kod har klassen LIST
en formell Generisk parameter G
de formella generiska parametrarna är platshållare för godtyckliga klassnamn som kommer att levereras när en deklaration av den generiska klassen görs, vilket visas i de två generiska härledningarna nedan, där ACCOUNT
och DEPOSIT
är andra klassnamn. ACCOUNT
och DEPOSIT
betraktas som faktiska generiska parametrar eftersom de ger riktiga klassnamn att ersätta G
I faktisk användning.,
list_of_accounts: LIST -- Account list list_of_deposits: LIST -- Deposit list
inom Eiffeltypssystemet, även om klass LIST
anses vara en klass, anses den inte vara en typ. En generisk härledning av LIST
som LIST
anses dock vara en typ.
begränsad genericityEdit
för listklassen som visas ovan kan en faktisk Generisk parameter som ersätterG
vara vilken annan tillgänglig klass som helst., För att begränsa den uppsättning klasser från vilka giltiga faktiska generiska parametrar kan väljas kan en generisk begränsning anges. I deklarationen för klassen SORTED_LIST
nedan dikterar den generiska begränsningen att en giltig faktisk Generisk parameter kommer att vara en klass som ärver från klassen COMPARABLE
. Den generiska begränsningen säkerställer att element i enSORTED_LIST
faktiskt kan sorteras.,
class SORTED_LIST
generika i JavaEdit
stöd för generiken, eller ”containers-of-type-t” lades till Java programmeringsspråket 2004 som en del av J2SE 5.0. I Java kontrolleras generics endast vid kompileringstid för typkorrekthet. Den generiska typinformationen tas sedan bort via en process som kallas typ radering, för att upprätthålla kompatibilitet med gamla JVM-implementeringar, vilket gör den otillgänglig vid körning., Till exempel konverteras en lista<String> till listan med råtyp. Kompilatorn infogar typ casts för att konvertera elementen till Strängtypen när de hämtas från listan, vilket minskar prestanda jämfört med andra implementeringar som C++ – mallar.
Genericitet i.Net Edit
Generics tillsattes som en del av. Net Framework 2.0 i November 2005, baserat på en forskningsprototyp från Microsoft Research startade 1999. Även om liknar generika i Java,.,NET generics gäller inte Typ radering, men implementera generics som en förstklassig mekanism i runtime med reification. Detta designval ger ytterligare funktionalitet, såsom att tillåta reflektion med bevarande av generiska typer, samt lindra några av begränsningarna av radering (som att inte kunna skapa generiska arrayer). Detta innebär också att det inte finns någon prestanda hit från runtime kastar och normalt dyra boxning omvandlingar., När primitiva och värdetyper används som generiska argument får de specialiserade implementeringar, vilket möjliggör effektiva generiska samlingar och metoder. Som i C++ och Java, kapslade generiska typer som ordbok<string, List<int>> är giltiga typer, men rekommenderas mot för medlemssignaturer i kodanalysdesignregler.
.,NET tillåter sex sorter av generiska typbegränsningar med hjälp av var nyckelordet, inklusive att begränsa generiska typer som värdetyper, att vara klasser, att ha konstruktörer och att implementera gränssnitt. Nedan följer ett exempel med en gränssnittsbegränsning:
MakeAtLeast () – metoden tillåter drift på arrayer, med element av generisk typ T. metodens typbegränsning indikerar att metoden är tillämplig på alla typer T som implementerar det generiska IComparable<t> – gränssnittet., Detta säkerställer ett kompileringsfel om metoden anropas om typen inte stöder jämförelse. Gränssnittet ger den generiska metoden CompareTo (T).
ovanstående metod kan också skrivas utan generiska typer, helt enkelt med hjälp av den icke-generiska Arraytypen. Eftersom matriser är kontravarianta, skulle gjutningen inte vara typ säker, och kompilatorn skulle inte kunna hitta vissa möjliga fel som annars skulle fångas vid användning av generiska typer. Dessutom skulle metoden behöva komma åt matrisobjekten som objekt istället, och skulle kräva gjutning för att jämföra två element., (För värdetyper som t.ex. int kräver detta en boxningskonvertering, även om detta kan hanteras med hjälp av Comparer<t> – klassen, vilket görs i standardinsamlingsklasserna.)
ett anmärkningsvärt beteende hos statiska medlemmar i en generisk.Net-klass är static member instantiation per körtidstyp (se exempel nedan).
Genericitet i DelphiEdit
Delphis objekt Pascal dialekt förvärvade generika i Delphi 2007-utgåvan, initialt endast med (nu avbruten) .,NET compiler innan de läggs till den ursprungliga koden i Delphi 2009 release. De semantik och kapacitet Delphi generics är till stor del modelleras på dem som hade av generics i. Net 2.0, även om genomförandet är av nödvändighet helt annorlunda. Här är en mer eller mindre direkt översättning av det första C# – exemplet som visas ovan:
som med C# kan metoder samt hela typer ha en eller flera typparametrar. I exemplet är TArray en generisk typ (definierad av språket) och gör minst en generisk metod., De tillgängliga begränsningarna är mycket lik de tillgängliga begränsningarna i C#: vilken värdetyp som helst, vilken klass som helst, en specifik klass eller gränssnitt och en klass med en parameterlös konstruktör. Flera begränsningar fungerar som en additiv union.
Genericitet i Free PascalEdit
Free Pascal implementerade generika före Delphi, och med olika syntax och semantik. Men eftersom FPC version 2.6.0, Delphi-stil syntax är tillgänglig när du använder {$mode Delphi} språkläge. Således kan Free Pascal programmerare använda generika i vilken stil de föredrar.,
Delphi och Free Pascal exempel:
funktionell languagesEdit
Genericitet i HaskellEdit
typklassmekanismen för Haskell stöder Generisk programmering.Sex av de fördefinierade typklasserna i Haskell (inklusive Eq, de typer som kan jämföras för jämlikhet, och visa, de typer vars värden kan återges som strängar) har den speciella egenskapen att stödja härledda instanser., Detta innebär att en programmerare som definierar en ny typ kan ange att denna typ är att vara en instans av en av dessa speciella typklasser, utan att tillhandahålla implementeringar av klassmetoderna som vanligtvis är nödvändiga vid deklarering av klassinstanser. Alla nödvändiga metoder kommer att ”härledas” – det vill säga konstrueras automatiskt-baserat på strukturen av typen.,I följande förklaring av en typ av binära träd anges till exempel att det ska vara en instans av klasserna Eq och Show:
data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show)
detta resulterar i att en jämställdhetsfunktion (==) och en strängrepresentationsfunktion (show) automatiskt definieras för vilken typ av form BinTree T som helst, förutsatt att t själv stöder dessa operationer.,
stödet för härledda instanser av Eq och Show gör deras metoder = = och visar generiska på ett kvalitativt annorlunda sätt än para-metriskt polymorfa funktioner: dessa ”funktioner” (mer exakt, typindexerade familjer av funktioner) kan tillämpas på värden av olika slag, och även om de beter sig annorlunda för varje argumenttyp behövs lite arbete för att lägga till stöd för en ny typ. Ralf Hinze (2004) har visat att en liknande effekt kan uppnås för användardefinierade typklasser med vissa programmeringstekniker., Andra forskare har föreslagit tillvägagångssätt för denna och andra typer av genericitet i samband med Haskell och förlängningar till Haskell (diskuteras nedan).
PolyPEdit
PolyP var den första generiska programmeringsspråket förlängning Haskell. I PolyP kallas generiska funktioner polytypiska. Språket introducerar en speciell konstruktion där sådana polytypiska funktioner kan definieras via strukturell induktion över strukturen av mönstret functor av en vanlig datatyp. Vanliga datatyper i PolyP är en delmängd av Haskell datatyper., En vanlig datatyp t måste vara av typen*→*, och om A är den formella typen argumentet i definitionen, då alla rekursiva samtal till t måste ha formen T A. dessa begränsningar utesluter högre slag datatyper samt kapslade datatyper, där rekursiva samtal är av en annan form.Den platta funktionen i PolyP finns här som ett exempel:
Generic HaskellEdit
Generic Haskell är en annan förlängning till Haskell, utvecklad vid Utrecht University i Nederländerna., De tillägg som den tillhandahåller är:
- Typindexerade värden definieras som ett värde indexerat över de olika Haskell-typkonstruktörerna (enhet, primitiva typer, summor, produkter och användardefinierade typkonstruktörer). Dessutom kan vi också specificera beteendet hos en typindexerade värden för en specifik konstruktör med konstruktörskalor och återanvända en generisk definition i en annan med standardfall.
det resulterande typindexerade värdet kan vara specialiserat på vilken typ som helst.
- Kindindexerade typer är typer indexerade över slag, definierade genom att ge ett kundcase för både * och k → k’., Instanser erhålls genom att tillämpa typindexerad typ till ett slag.
- generiska definitioner kan användas genom att tillämpa dem på en typ eller typ. Detta kallas Generisk applikation. Resultatet är en typ eller ett värde, beroende på vilken typ av generisk definition som tillämpas.
- Generisk abstraktion gör det möjligt att definiera generiska definitioner genom att abstrahera en typparameter (av ett visst slag).
- typindexerade typer är typer som indexeras över typkonstruktörerna. Dessa kan användas för att ge typer till mer involverade generiska värden., De resulterande typindexerade typerna kan vara specialiserade på vilken typ som helst.
som ett exempel, jämställdhetsfunktionen i Generisk Haskell:
CleanEdit
Clean erbjuder Generisk programmering baserad PolyP och generisk Haskell som stöds av GHC>=6,0. Det parametrizes av slag som de men erbjuder överbelastning.
andra språkRedigera
ML-familjen av programmeringsspråk stöder Generisk programmering genom parametrisk polymorfism och generiska moduler som kallas funktionärer.,Både Standard ML och OCaml tillhandahåller funktionärer, som liknar klassmallar och Adas generiska paket. Scheme syntaktiska abstraktioner har också en anslutning till genericitet – det här är faktiskt en superset av mallning à la c++.
en Verilog-modul kan ta en eller flera parametrar, till vilka deras faktiska värden tilldelas vid instansiering av modulen. Ett exempel är en generisk registermatris där matrisbredden ges via en parameter., En sådan array, i kombination med en generisk trådvektor, kan göra en generisk buffert eller minnesmodul med en godtycklig bitbredd ur en enda modulimplementering.
VHDL, som härrör från Ada, har också generiska funktioner.
Lämna ett svar