C++ mallar

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 30 mars 2016; kontroller kräver 29 redigeringar .

Templates ( eng.  template ) är ett C++- språkverktyg utformat för att koda generaliserade algoritmer , utan att vara bundna till vissa parametrar (till exempel datatyper , buffertstorlekar, standardvärden).

I C++ är det möjligt att skapa funktions- och klassmallar .

Mallar låter dig skapa parametriserade klasser och funktioner. Parametern kan vara vilken typ som helst eller ett värde av en av de tillåtna typerna (heltal, enum, pekare till vilket objekt som helst med ett globalt tillgängligt namn, referens). Till exempel behöver vi någon klass:

klass SomeClass { int SomeValue ; int SomeArray [ 20 ]; ... };

För ett specifikt syfte kan vi använda den här klassen. Men, plötsligt har målet ändrats lite, och en annan klass behövs. Nu behöver vi 30 arrayelement SomeArrayoch en riktig SomeValueelementtyp SomeArray. Då kan vi abstrahera bort från konkreta typer och använda mallar med parametrar. Syntax: i början, innan vi deklarerar klassen, deklarerar vi mallen, det vill templatesäga vi anger parametrarna inom vinkelparenteser. I vårt exempel:

mall < int ArrayLength , typnamn SomeValueType > class SomeClass { SomeValueType SomeValue ; SomeValueType SomeArray [ ArrayLength ]; ... };

Sedan för det första fallet (med heltal SomeValue och SomeArray med 20 element) skriver vi:

SomeClass < 20 , int > SomeVariable ;

för den andra:

SomeClass < 30 , double > SomeVariable2 ;

Även om mallar tillhandahåller en förkortning för en bit kod, förkortar användningen av dem faktiskt inte den körbara koden, eftersom kompilatorn skapar en separat instans av en funktion eller klass för varje uppsättning alternativ. Som ett resultat försvinner möjligheten att dela kompilerad kod inom delade bibliotek.

Funktionsmallar

Mall Beskrivning Syntax

En funktionsmall börjar med nyckelordet templateföljt av en lista med parametrar inom vinkelparenteser. Sedan kommer funktionsdeklarationen:

mall < typnamn T > void sort ( T array [], int size ); // prototyp: sorteringsmall är deklarerad men inte definierad mall < typnamnT > _ void sort ( T array [], int size ) // deklaration och definition { Tt ; _ för ( int i = 0 ; i < storlek - 1 ; i ++ ) för ( int j = storlek - 1 ; j > i ; j -- ) if ( array [ j ] < array [ j -1 ]) { t = array [ j ]; array [ j ] = array [ j -1] ; array [ j - 1 ] = t ; } } mall < int BufferSize > // heltalsparameter char * read () { char * Buffer = ny char [ BufferSize ]; /* läs data */ returnera buffert ; }

Nyckelordet är typenamerelativt nytt, så standarden [1] tillåter användning classistället för typename:

mall < classT > _

Istället för T är vilken annan identifierare som helst acceptabel.

Användningsexempel

Det enklaste exemplet är bestämning av minsta två kvantiteter.

Om a är mindre än b, returnera a, annars returnera b

I avsaknad av mallar måste programmeraren skriva separata funktioner för varje datatyp som används. Även om många programmeringsspråk definierar en inbyggd minimifunktion för elementära typer (som heltal och reella tal), kan en sådan funktion behövas för komplexa (till exempel "tid" eller "sträng") och mycket komplexa (" spelare” i ett onlinespel ) objekt .

Så här ser minimifunktionsmallen ut:

mall < typnamnT > _ T min ( T a , T b ) { returnera a < b ? a : b ; }

För att anropa den här funktionen kan du helt enkelt använda dess namn:

min ( 1,2 ) ; _ min ( 'a' , 'b' ); min ( sträng ( "abc" ), sträng ( "cde" ) );

Mallfunktionsanrop

Generellt sett, för att anropa en mallfunktion måste du ange värden för alla mallparametrar. För att göra detta, efter mallnamnet, anges en lista med värden inom vinkelparenteser:

int i [] = { 5 , 4 , 3 , 2 , 1 }; sortera < int > ( i , 5 ); char c [] = "bvgda" ; sortera < char > ( c , strlen ( c ) ); sortera < int > ( c , 5 ); // error: sort<int> har en int[]-parameter, inte ett char[] char * ReadString = läs < 20 > (); ta bort [] ReadString ; ReadString = läs < 30 > ();

För varje uppsättning alternativ genererar kompilatorn en ny instans av funktionen. Processen att skapa en ny instans kallas mallinstansiering .

I exemplet ovan skapade kompilatorn två funktionsmallspecialiseringar sort(för typerna charoch int) och två mallspecialiseringar read(för värdena BufferSize20 och 30). Det senare är med största sannolikhet slösaktigt, eftersom kompilatorn för varje möjligt värde på parametern kommer att skapa fler och fler nya instanser av funktioner som bara skiljer sig med en konstant.

Härledning av parametervärden

I vissa fall kan kompilatorn härleda (logiskt bestämma) värdet på en funktionsmallparameter från ett funktionsargument. Till exempel, när du anropar funktionen som beskrivs ovan, är det sortinte nödvändigt att ange mallparametern (om den matchar typen av elementen i arrayargumentet):

int i [ 5 ] = { 5 , 4 , 3 , 2 , 1 }; sortera ( i , 5 ); // ring sortera<int> char c [] = "bvgda" ; sortera ( c , strlen ( c ) ); // ring sortera<char>

Borttagning är också möjligt i mer komplexa fall .

I fallet med användning av klassmallar med heltalsparametrar är det också möjligt att härleda dessa parametrar. Till exempel:

mall < int storlek > klass IntegerArray { int Array [ storlek ]; /* ... */ }; mall < int size > // Template prototype void PrintArray ( IntegerArray < size > array ) { /* ... */ } // Template call // Använda mallobjektet IntegerArray < 20 > ia ; PrintArray ( ia );

Slutledningsregler introduceras i språket för att göra det lättare att använda en mall och för att undvika eventuella fel, som att försöka sort< int >sortera en rad tecken.

Om en mallparameter kan härledas från flera argument, måste resultatet av slutledningen vara exakt detsamma för alla dessa argument. Till exempel är följande anrop felaktiga:

min ( 0 , 'a' ); min ( 7 , 7,0 );

Misstag i mallar

Fel associerade med användningen av specifika mallparametrar kan inte upptäckas innan mallen används. Till exempel mininnehåller själva mallen inga fel, men att använda den med typer för vilka operationen '<'inte är definierad kommer att resultera i ett fel:

struktur A { int a ; }; A obj1 , obj2 ; min ( objl , obj2 );

Om du går in i operationen '<'före den första användningen av mallen, kommer felet att elimineras. Så här visar sig flexibiliteten hos mallar i C++ :

friend inline bool operator < ( const A & a1 , const A & a2 ) { return a1 . a < a2 . a ; } min ( objl , obj2 );

Klassmallar

I en klass som implementerar en länkad lista med heltal, beror algoritmerna för att lägga till ett nytt element i listan och söka efter det önskade elementet inte av att elementen i listan är heltal. Samma algoritmer skulle gälla för en lista med karaktärer, strängar, datum, spelarklasser och så vidare.

mall < classT > _ klasslista _ { /* ... */ offentliga : void Lägg till ( const T & Element ); bool Hitta ( const T & Element ); /* ... */ };

Använda mallar

För att använda en klassmall måste du ange dess parametrar:

Lista < int > li ; Lista < sträng > ls ; li . lägg till ( 17 ); ls . Lägg till ( "Hej!" );

Tekniska detaljer

Mallalternativ

Mallparametrar kan vara: typparametrar, vanliga typparametrar, mallparametrar.

Du kan ange standardvärden för parametrar av vilken typ som helst.

mall < klass T1 , // typ parameter typnamn T2 , // typ parameter int I , // normal typ parameter T1 DefaultValue , // normal typ parameter mall < klass > klass T3 , // mall parameter klass Character = char // default parameter > Mallparametrar

Om det är nödvändigt att använda samma mall i en klass- eller funktionsmall, men med olika parametrar, används mallparametrar. Till exempel:

mall < klass Typ , mall < klass > klass Container > klass CrossReferences { Behållare < Typ > mems ; Behållare < Typ * > refs ; /* ... */ }; Korsreferenser < Datum , vektor > cr1 ; CrossReferences < string , set > cr2 ;

Funktionsmallar kan inte användas som mallparametrar.

Regler för att härleda funktionsmallargument

För parametrar som är typer (till exempel T-parametern för sorteringsfunktionen) är slutledning möjlig om funktionsargumentet är av en av följande typer:

Typ av argument Beskrivning
T
const T
volatile T
Själva typen T, eventuellt med modifierare consteller volatile. mall < classT > _ T ReturnMe ( const T arg ) { return arg ; } ReturnMe ( 7 ); ReturnMe ( 'a' );
T*
T&
T[A]
A är en konstant
En pekare, referens eller array av element av typen T.

Ett exempel är sorteringsfunktionsmallen som diskuteras ovan.

Templ<T>
Templ - klassmallnamn
Som ett argument kräver funktionen en specifik specialisering av någon mall. #inkludera <vektor> mall < classT > _ void sort ( vektor < T > array ) { /* sort */ } vektor < int > i ; vektor < char > c ; sortera ( i ); sortera ( c );
T (*) (args)
args - några argument
Pekare till en funktion som returnerar typ T. mall < classT > _ T * CreateArray ( T ( * GetValue )(), const int size ) { T * Array = ny T [ storlek ]; för ( int i = 0 ; i < storlek ; i ++ ) Array [ i ] = GetValue (); return Array ; } int GetZero () { return 0 ; } char InputChar () { röding c ; cin >> c ; returnera c ; } int * ArrayOfZeros = CreateArray ( GetZero , 20 ); char * String = CreateArray ( InputChar , 40 );
type T::*
T Class::*
typ - någon typ
Klass - någon klass
En pekare till en medlem av klassen T av en godtycklig typ.
Pekare till en medlem av typ T av en godtycklig klass. klass MyClass { offentliga : int a ; }; mall < classT > _ T & IncrementIntegerElement ( int T ::* Element , T & Object ) { objekt . * Element += 1 ; returnera Objekt ; } mall < classT > _ T IncrementMyClassElement ( T MyClass ::* Element , MyClass & Object ) { objekt . * Element += 1 ; returnera objekt . * Element ; } MyClass Obj ; int n ; n = ( IncrementIntegerElement ( & MyClass :: a , Obj ) ). a ; n = IncrementMyClassElement ( & MyClass :: a , Obj );
type (T::*) (args)
T (Class::*) (args)
typ - någon typ
Klass - någon klass
args - några argument
Pekare till en medlemsfunktion av klass T av godtycklig typ.
Pekare till en medlemsfunktion av typ T av en godtycklig klass. klass MyClass { offentliga : int a ; int IncrementA (); }; int MyClass::IncrementA () { return ++ a ; } mall < classT > _ T & CallIntFunction ( int ( T ::* Function )(), T & Object ) { ( Objekt . * Funktion )(); returnera Objekt ; } mall < classT > _ T CallMyClassFunction ( T ( MyClass ::* Function )(), MyClass & Object ) { return ( Objekt . * Funktion )(); } MyClass Obj ; int n ; n = ( CallIntFunction ( & MyClass :: IncrementA , Obj ) ). a ; n = CallMyClassFunction ( & MyClass :: IncrementA , Obj );

Medlemmar av mallklasser

Medlemmar av en klassmall är mallar, och med samma parametrering som klassmallen. Detta innebär i synnerhet att definitionen av medlemsfunktioner bör börja med mallhuvudet:

mall < classT > _ klass A { void f ( T- data ); void g ( void ); offentliga : A (); }; mall < classT > _ void A < T >:: f ( T data ); mall < classT > _ void A < T >:: g ( void );

Inom mallens räckvidd behöver specifikationen inte upprepas. Det betyder att till exempel A<T>::A() är en konstruktor , även om du också kan skriva A<T>::A<T>().

Typer som medlemmar i klasser

Om mallparametern är en klass som har en medlem som är av datatyp , måste nyckelordet användas för att använda den medlemmen typename. Till exempel:

klass Container { offentliga : int array [ 15 ]; typedef int * iterator ; /* ... */ iterator start () { return array ; } }; mall < klass C > void f ( C & vektor ) { C :: iterator i = vektor . börja (); // fel typnamn C :: iterator i = vektor . börja (); } Mallar som medlemmar i klasser

Det finns också problem med mallmedlemmar. Om en mall (ConvertTo()), som är medlem i en klass (A), som i sin tur är en mallparameter (f), används i denna mall (f) och inte tillåter inferens av parametrar, då måste användas template:

klass A { /* ... */ offentliga : mall < klass T > T & ConvertTo (); mall < klass T > void ConvertFrom ( const T & data ); }; mall < classT > _ void f ( T Container ) { int i1 = Behållare . mall ConvertTo < int > () + 1 ; behållare . ConvertFrom ( i1 ); // ingen kvalificering behövs }

Kritik och jämförelse med alternativ

Mallmetaprogrammering i C++ lider av många begränsningar, inklusive portabilitetsproblem, brist på felsökning eller I/O-stöd under mallinstansiering, långa kompileringstider, dålig kodläsbarhet, dålig feldiagnostik och oklara felmeddelanden [2] . C++ mallundersystemet definieras som ett Turing-komplett rent funktionellt programmeringsspråk, men programmerare i funktionell stil ser detta som en provokation och är ovilliga att erkänna C++ som ett framgångsrikt språk [3] .

Många språk ( Java 5, Ada , Delphi 2009) implementerar generiskt programmeringsstöd på ett enklare sätt, några till och med på typsystemnivå (se Eiffel och parametrisk polymorfism i ML- familjen av språk ); sådana språk behöver inte mekanismer som liknar C++-mallar.

C:s makroersättningsfaciliteter, även om de inte är kompletta med Turing, är tillräckliga för lågnivåprogrammering i generativ programmering , och deras kapacitet har utökats avsevärt i C99 .

D - språket har mallar som är kraftfullare än C++. [4] .

Se även

Anteckningar

  1. C++ Standard "Standard för programmeringsspråket C++": ISO/IEC 14882 1998 .
  2. K. Czarnecki, J. O'Donnell, J. Striegnitz, W. Taha. DSL-implementering i metaocaml, mall haskell och C++ . — University of Waterloo, University of Glasgow, Research Centre Julich, Rice University, 2004. .
    Citat: C++ Template Metaprogramming lider av ett antal begränsningar, inklusive portabilitetsproblem på grund av kompilatorbegränsningar (även om detta har förbättrats avsevärt under de senaste åren), brist på felsökningsstöd eller IO under mallinstansiering, långa kompileringstider, långa och obegripliga fel , dålig läsbarhet för koden och dålig felrapportering.
  3. Sheard T., Jones SP Mall Metaprogrammering för Haskell  // Haskell Workshop. - Pittsburgh: ACM 1-58113-415-0/01/0009, 2002. .
    Citat från Robinsons provocerande papper identifierar C++-mallar som en stor, om än oavsiktlig, framgång för C++-språkdesignen. Trots den extremt barocka karaktären hos mallmetaprogrammering, används mallar på fascinerande sätt som sträcker sig bortom språkdesignernas vildaste drömmar. Kanske överraskande, med tanke på att mallar är funktionella program, har funktionella programmerare varit långsamma med att dra nytta av C++s framgångar
  4. ↑ Digital Mars : D-programmeringsspråk 2.0  

Litteratur

  • David Vandevoerd, Nicholas M. Josattis. C ++-mallar: Den kompletta guiden = C++-mallar: Den kompletta guiden. - M . : "Williams" , 2003. - S.  544 . — ISBN 0-201-73484-2 .
  • Podbelsky V. V. 6.9. Funktionsmallar //Kapitel 6. Funktioner, tips, referenser // C++-språk / recension. Dadaev Yu. G. - 4. - M . : Finans och statistik , 2003. - S. 230-236. — 560 sid. - ISBN 5-279-02204-7 , UDC 004.438Si (075.8) LBC 32.973.26-018 1ya173.

Länkar