De definitie van een sjabloon voor een bepaald type parameter wordt genoemd. Sjabloonfuncties

17 maart 2009 om 20:36

C ++ Sjabloonspecialisatietrucs

  • C ++

Sjablonenspecialisatie is een van de "complexe" kenmerken van de C++-taal en wordt voornamelijk gebruikt bij het maken van bibliotheken. Helaas worden sommige specifieke kenmerken van sjabloonspecialisatie niet goed behandeld in populaire boeken over deze taal. Bovendien beschrijven zelfs de 53 pagina's van de officiële ISO-taalstandaard die aan sjablonen zijn gewijd, interessante details in een rommelige vorm, waardoor er veel overblijft om "zelf te raden - het is duidelijk". Onder de snit heb ik geprobeerd de basisprincipes van sjabloonspecialisatie duidelijk te vermelden en te laten zien hoe deze principes kunnen worden gebruikt bij de constructie van magische spreuken.

Hallo Wereld

Hoe zijn we gewend om sjablonen te gebruiken? Gebruik het sjabloonsleutelwoord gevolgd door de namen tussen punthaken sjabloon parameters gevolgd door het type en de naam. Bij parameters geven ze ook aan wat het is: een type (typenaam) of een waarde (bijvoorbeeld int). Het type sjabloon zelf kan een klasse (klasse), structuur (struct is eigenlijk een klasse) of functie (bool foo () enzovoort) zijn. De eenvoudigste sjabloonklasse "A" kan bijvoorbeeld als volgt worden gedefinieerd:

Na een tijdje willen we dat onze klas voor alle typen hetzelfde werkt, maar anders voor iets lastigs als int. Bullshit-vraag, een specialisatie schrijven: het ziet er hetzelfde uit als een advertentie, maar parameters we geven de sjabloon niet tussen punthaken aan, in plaats daarvan geven we specifieke aan argumenten sjabloon achter de naam:

Sjabloon<>klasse A, eerste klasse< int >(); // int hier is het sjabloonargument
Als u klaar bent, kunt u methoden en velden van een speciale implementatie voor int schrijven. Deze specialisatie wordt meestal compleet(volledige specialisatie of expliciete specialisatie). Voor de meeste praktische taken is niet meer nodig. En indien nodig, dan...

Aangepaste sjabloon is een nieuwe sjabloon

Als u de ISO C ++-standaard aandachtig leest, kunt u een interessante verklaring vinden: door een gespecialiseerde sjabloonklasse te maken, creëren we nieuwe sjabloonklas(14.5.4.3). Wat levert het ons op? Een gespecialiseerde sjabloonklasse kan methoden, velden of typedeclaraties bevatten die niet voorkomen in de sjabloonklasse die we specialiseren. Het is handig wanneer u een sjabloonklasse-methode nodig hebt om alleen voor een specifieke specialisatie te werken - het is voldoende om de methode alleen in deze specialisatie te declareren, de compiler doet de rest:

Een gespecialiseerde sjabloon kan zijn eigen sjabloonparameters hebben

De duivel zit, zoals u weet, in de details. Het feit dat een gespecialiseerde sjabloonklasse een geheel nieuwe en aparte klasse is, is zeker interessant, maar er zit niet veel magie in. En de magie is in een onbeduidende consequentie - als het een afzonderlijke sjabloonklasse is, dan kan deze afzonderlijke hebben, op geen enkele manier verbonden met een niet-gespecialiseerde sjabloonklasse. parameters(parameters zijn wat na sjabloon tussen punthaken staat). Bijvoorbeeld als volgt:

Sjabloon< typename S, typename U >klasse A, eerste klasse< int > {};
Toegegeven, dit is precies de code van de compiler wil niet compileren- we gebruiken niet de nieuwe sjabloonparameters S en U, wat verboden is voor een gespecialiseerde sjabloonklasse (maar de gespecialiseerde compiler begrijpt dat dit een klasse is omdat deze dezelfde naam "A" heeft als de reeds gedeclareerde sjabloonklasse). De compiler zal zelfs een speciale fout zeggen: "expliciete specialisatie gebruikt gedeeltelijke specialisatiesyntaxis, gebruik sjabloon<>in plaats van ". Het geeft aan dat als er niets te zeggen is, je een sjabloon moet gebruiken<>en niet opscheppen. Waarom kunnen dan nieuwe parameters worden gebruikt in een gespecialiseerde sjabloonklasse? Het antwoord is vreemd - om te vragen argumenten specialisaties (argumenten zijn wat er achter de klassenaam tussen punthaken staat). Dat wil zeggen, door een sjabloonklasse te specialiseren, kunnen we, in plaats van een eenvoudige en begrijpelijke int, deze specialiseren via nieuwe parameters:

Sjabloon< typename S, typename U >klasse A, eerste klasse< std::map< S, U > > {};
Zo'n vreemde notatie zal compileren. En wanneer de resulterende sjabloonklasse wordt gebruikt met std :: map, wordt een specialisatie gebruikt, waarbij het sleuteltype std :: map beschikbaar zal zijn als een parameter van de nieuwe sjabloon S, en het std :: mapwaardetype als U.

Een dergelijke specialisatie van de sjabloon, waarin een nieuwe lijst met parameters wordt ingesteld en via deze parameters argumenten voor de specialisatie worden ingesteld, wordt genoemd gedeeltelijke specialisatie(deelspecialisatie). Waarom "gedeeltelijk"? Blijkbaar omdat het oorspronkelijk bedoeld was als een syntaxis voor het specialiseren van een sjabloon, niet voor alle argumenten. Een voorbeeld waarbij een sjabloonklasse met twee parameters zich in slechts één ervan specialiseert (specialisatie werkt wanneer het eerste argument, T, is opgegeven als int. In dit geval kan het tweede argument van alles zijn - hiervoor is een nieuwe parameter U geïntroduceerd in de deelspecialisatie en wordt gespecificeerd in de lijst met argumenten voor specialisatie):

Sjabloon< typename T, typename S >klasse B (); sjabloon< typename U >klasse B< int, U > {};

De magische gevolgen van gedeeltelijke specialisatie

Er zijn een aantal interessante implicaties van de twee eigenschappen van sjabloonspecialisatie die hierboven zijn beschreven. Als u bijvoorbeeld gedeeltelijke specialisatie gebruikt, kunt u, door nieuwe sjabloonparameters te introduceren en gespecialiseerde argumenten erdoor te beschrijven, samengestelde typen opsplitsen in eenvoudigste. In het onderstaande voorbeeld wordt de gespecialiseerde sjabloonklasse A gebruikt als de sjabloonargumenten van het type functieaanwijzer zijn. Tegelijkertijd kunt u via de nieuwe parameters van de sjabloon S en U het type van de geretourneerde waarde van deze functie en het type van zijn argument krijgen:

Sjabloon< typename S, typename U >klasse A, eerste klasse< S(*)(U) > {};
En als u typedef of static const int in een gespecialiseerde sjabloon declareert (gebruik makend van het feit dat dit een nieuwe sjabloon is), dan kunt u deze gebruiken om de benodigde informatie uit het type te halen. We gebruiken bijvoorbeeld een sjabloonklasse om objecten op te slaan en willen de grootte van het doorgegeven object krijgen, of 0 als het een aanwijzer is. In twee regels:

Sjabloon< typename T >struct Get (const static int Grootte = sizeof (T);); sjabloon< typename S >struct Get< S* >(const static int Grootte = 0;); Krijgen< int >:: Maat // bijv. 4 Get< int* >:: Grootte // 0 - vond de aanwijzer :)
Dit soort magie wordt voornamelijk gebruikt in bibliotheken: stl, boost, loki, enzovoort. Bij programmeren op hoog niveau is het gebruik van dergelijke trucs natuurlijk omslachtig - ik denk dat iedereen zich de constructie herinnert om de grootte van een array te krijgen :). Maar in bibliotheken maakt gedeeltelijke specialisatie het relatief eenvoudig om gedelegeerden, evenementen, complexe containers en andere soms zeer noodzakelijke en nuttige dingen te implementeren.

Collega's, als u een fout vindt (en ik ben helaas geen goeroe - ik kan het mis hebben) of u heeft kritiek, vragen of aanvullingen op het bovenstaande, dan zal ik graag reageren.

Update: Beloofd vervolg

Een sjabloonfunctie definieert een algemene reeks bewerkingen die op verschillende soorten gegevens worden toegepast. Met behulp van dit mechanisme kunnen enkele algemene algoritmen worden toegepast op een breed scala aan gegevens. Zoals u weet, zijn veel algoritmen logisch hetzelfde, ongeacht het type gegevens waarmee ze werken. Het Quicksort-algoritme is bijvoorbeeld hetzelfde voor een array van gehele getallen en voor een array van getallen met drijvende komma. Alleen het type gegevens dat moet worden gesorteerd, verschilt. Door een generieke functie te creëren, kunt u de essentie van een algoritme definiëren, ongeacht het gegevenstype. De compiler genereert vervolgens automatisch de juiste code voor het gegevenstype waarvoor deze specifieke functie-implementatie is gemaakt tijdens het compileren. In wezen, wanneer een sjabloonfunctie wordt gemaakt, wordt een functie gemaakt die zichzelf automatisch kan overbelasten.

Sjabloonfuncties worden gemaakt met behulp van het sjabloonsleutelwoord. De gebruikelijke betekenis van het woord "sjabloon" weerspiegelt volledig het gebruik ervan in C ++. De sjabloon wordt gebruikt om het skelet van de functie te maken, waarbij de implementatiedetails aan de compiler worden overgelaten. De algemene vorm van een sjabloonfunctie is als volgt:

sjabloon return_type functienaam (parameterlijst)
{
// functie lichaam
}

Hier is ptyp een typeparameter, een tijdelijke aanduiding voor de gegevenstypenaam die door de functie wordt gebruikt. Deze typeparameter kan worden gebruikt in een functiedefinitie. Dit is echter slechts een "plaatshouder" die automatisch door de compiler wordt vervangen door het werkelijke gegevenstype bij het maken van een specifieke versie van de functie.

Hieronder ziet u een kort voorbeeld waarmee een sjabloonfunctie wordt gemaakt die twee parameters heeft. Deze functie verandert de waarden van de waarden van deze parameters onderling. Aangezien het algemene proces van het uitwisselen van waarden tussen twee variabelen niet afhankelijk is van hun type, kan het natuurlijk worden geïmplementeerd met behulp van een sjabloonfunctie.

// voorbeeld van een functiesjabloon
#erbij betrekken
// functiesjabloon
sjabloon void swap (X & a, X & b)
{
X-temp;
temperatuur = een;
een = b;
b = temperatuur;
}
int hoofd ()
{
int i = 10, j = 20;
zweven x = 10,1, y = 23,3;
char a = "x", b = "z";
cout<< "Original i, j: " << i << " " << j << endl;
cout<< "Original x, y: " << x << " " << у << endl;
cout<< "Original a, b: " << a << " " << b << endl;
verwisselen (i, j); // wissel gehele getallen om
omwisselen (x, y); // uitwisseling van echte waarden
verwisselen (a, b); // karakter wisselen
cout<< "Swapped i, j: " << i << " " << j << endl;
cout<< "Swapped x, y: " << x << " " << у << endl;
cout<< "Swapped a, b: " << a << " " << b << endl;
retourneer 0;
}

Laten we dit programma eens nader bekijken. Lijn

Sjabloon void swap (X & a, X & b)

Geeft aan de compiler aan dat er een sjabloon wordt gegenereerd. Hier is X een typesjabloon dat wordt gebruikt als een typeparameter. Het volgende is de verklaring van de functie swap () met behulp van het gegevenstype X voor die parameters die waarden zullen uitwisselen. In de hoofdfunctie () wordt de functie swap () genoemd om er drie verschillende soorten gegevens aan door te geven: gehele getallen, getallen met drijvende komma en tekens. Aangezien de functie swap () een sjabloonfunctie is, maakt de compiler automatisch drie verschillende versies van de functie swap () - een voor het werken met gehele getallen, een voor het werken met drijvende-komma-getallen en tot slot een voor het werken met variabelen van een teken type.

Les 29. Functiesjablonen gebruiken

Bij het maken van functies doen zich soms situaties voor waarin twee functies dezelfde verwerking uitvoeren, maar op verschillende gegevenstypen werken (de ene gebruikt bijvoorbeeld parameters van het type int en de andere van het type float). Zoals je al weet uit les 13, kun je met behulp van het functie-overbelastingsmechanisme dezelfde naam gebruiken voor functies die verschillende acties uitvoeren en verschillende soorten parameters hebben. Als functies echter waarden van verschillende typen retourneren, moet u er unieke namen voor gebruiken (zie de opmerking bij les 13). Stel bijvoorbeeld dat u een functie met de naam max hebt die het maximum van twee gehele waarden retourneert. Als u later een vergelijkbare functie nodig hebt die het maximum van twee drijvende-kommawaarden retourneert, moet u een andere functie definiëren, zoals fmax. In deze zelfstudie leert u hoe u C++-sjablonen gebruikt om snel functies te maken die verschillende soorten waarden retourneren. Aan het einde van deze les heb je de volgende basisconcepten onder de knie:

    Een sjabloon definieert een reeks instructies waarmee uw programma's later meerdere functies kunnen creëren.

    Programma's gebruiken vaak functiesjablonen om snel meerdere functies te definiëren die, met behulp van dezelfde operators, op verschillende parametertypen werken of verschillende retourtypen hebben.

    Functiesjablonen hebben specifieke namen die overeenkomen met de functienaam die u in uw programma gebruikt.

    Nadat uw programma een functiesjabloon heeft gedefinieerd, kan het vervolgens een specifieke functie maken met behulp van die sjabloon om een ​​prototype te definiëren dat de naam van de sjabloon, de retourwaarde van de functie en parametertypen bevat.

    Tijdens het compileren maakt de C++-compiler functies in uw programma met behulp van de typen die zijn gespecificeerd in de functie-prototypen die verwijzen naar de sjabloonnaam.

Functiesjablonen hebben een unieke syntaxis die op het eerste gezicht verwarrend kan zijn. Na het maken van een of twee sjablonen, zult u echter merken dat ze eigenlijk heel gemakkelijk te gebruiken zijn.

MAAK EEN EENVOUDIG FUNCTIEPATROON

Een functiesjabloon definieert een typeonafhankelijke functie. Met behulp van zo'n sjabloon kunnen uw programma's later specifieke functies definiëren met de vereiste typen. Het volgende is bijvoorbeeld een sjabloon voor een functie met de naam max die de grootste van twee waarden retourneert:

sjabloon Tmax (Ta, Tb)

(if (a> b) retourneer (a); anders retourneer (b);)

De letter T staat in dit geval voor een generiek sjabloontype. Na het definiëren van een sjabloon in uw programma, declareert u functie-prototypes voor elk type dat u nodig heeft. In het geval van de max-sjabloon creëren de volgende prototypen functies van het type float en int.

drijven max (zweven, drijven); int max (int, int);

Wanneer de C++-compiler deze prototypes tegenkomt, zal deze het sjabloontype T vervangen door het type dat u opgeeft bij het construeren van de functie. In het geval van het float-type zal de max-functie na vervanging de volgende vorm aannemen:

sjabloon Tmax (Ta, Tb)

(if (a> b) retourneer (a); anders retourneer (b);)

float max (float a, float b)

(if (a> b) retourneer (a); anders retourneer (b);)

Het volgende MAX_TEMR.CPP-programma gebruikt de max-sjabloon om een ​​functie van het type int en float te maken.

#erbij betrekken

sjabloon Tmax (Ta, Tb)

(if (a> b) retourneer (a); anders retourneer (b);)

drijven max (zweven, drijven);

int max (int, int);

(cout<< "Максимум 100 и 200 равен " << max(100, 200) << endl; cout << "Максимум 5.4321 и 1.2345 равен " << max(5.4321, 1.2345) << endl; }

Tijdens het compileren genereert de C++-compiler automatisch instructies voor het construeren van één functie die werkt op het type int en een tweede functie die werkt op het type float. Omdat de C++-compiler instructies beheert die overeenkomen met functies die u maakt met behulp van sjablonen, kunt u dezelfde namen gebruiken voor functies die verschillende soorten waarden retourneren. Je zou dit niet kunnen doen met alleen functieoverbelasting, zoals besproken in les 13.

Functiesjablonen gebruiken

Naarmate uw programma's complexer worden, kunnen er situaties zijn waarin u vergelijkbare functies nodig hebt die dezelfde bewerkingen uitvoeren, maar op verschillende gegevenstypen. Met een functiesjabloon kunnen uw programma's een generieke of typeonafhankelijke functie definiëren. Wanneer een programma een functie voor een specifiek type moet gebruiken, zoals int of float, specificeert het een functie-prototype dat de functiesjabloonnaam en de return- en parametertypes gebruikt. Tijdens het compileren zal C++ de corresponderende functie creëren. Door sjablonen te maken, vermindert u het aantal functies dat u zelf moet coderen en kunnen uw programma's dezelfde naam gebruiken voor functies die een specifieke bewerking uitvoeren, ongeacht de retourwaarde en parametertypen van de functie.

SJABLONEN DIE MEERDERE TYPES GEBRUIKEN

De vorige sjabloondefinitie voor de max-functie gebruikte één generiek type, T. Heel vaak zijn meerdere typen vereist in een functiesjabloon. De volgende instructies maken bijvoorbeeld een sjabloon voor de functie show_array, die de elementen van een array weergeeft. De sjabloon gebruikt type T om het type array te definiëren en type T1 om het type count-parameter aan te geven:

sjabloon

< count; index++) cout << array << " "; cout << endl; }

Net als voorheen moet het programma functie-prototypes specificeren voor de vereiste typen:

void show_array (int *, int); void show_array (float *, niet ondertekend);

Het volgende programma SHOW_TEM.CPP gebruikt een sjabloon om functies te maken die arrays van het type int en het type float uitvoeren.

#erbij betrekken

sjabloon void show_array (T * array, T1-telling)

(T1-index; voor (index = 0; index< count; index++) cout << array “ " "; cout << endl; }

void show_array (int *, int);

void show_array (float *, niet ondertekend);

(int pagina's = (100, 200, 300, 400, 500); zwevende prijzenH = (10.05, 20.10, 30.15); show_array (pagina's, 5); show_array (prijzen, 3);)

Sjablonen en meerdere typen

Naarmate functiesjablonen complexer worden, kunnen ze ondersteuning bieden voor meerdere typen. Uw programma kan bijvoorbeeld een sjabloon maken voor een functie met de naam array_sort die de elementen van een array sorteert. In dit geval kan de functie twee parameters gebruiken: de eerste, corresponderend met de array, en de tweede, corresponderend met het aantal elementen in de array. Als het programma aanneemt dat de array nooit meer dan 32767 waarden zal bevatten, kan het het type int gebruiken voor de parameter array size. Een meer generiek patroon zou het programma echter de mogelijkheid kunnen bieden om zijn eigen type voor deze parameter te specificeren, zoals hieronder getoond:

sjabloon t , klasse T1> void array_sort (T-array, T1-elementen)

(// exploitanten)

Met behulp van de array_sort-sjabloon kan een programma functies maken die kleine floats (minder dan 128 elementen) en zeer grote int-arrays sorteren met behulp van de volgende prototypes:

void array_sort (float, char); void array_sort (int, lang);

WAT JE MOET WETEN

Zoals u al weet, vermindert het gebruik van functiesjablonen de programmering doordat de C++-compiler instructies kan genereren voor functies die alleen verschillen in return- en parametertypes. In les 30 leert u hoe u sjablonen kunt gebruiken om typeonafhankelijke of generieke klassen te maken. Voordat u les 30 voltooit, moet u ervoor zorgen dat u de volgende basisconcepten beheerst:

      Met functiesjablonen kunt u typeonafhankelijke of generieke functies declareren.

      Wanneer uw programma een functie met bepaalde gegevenstypen moet gebruiken, moet het een functie-prototype leveren dat de vereiste typen definieert.

      Wanneer de C++-compiler zo'n functie-prototype tegenkomt, genereert hij instructies die overeenkomen met die functie, waarbij de vereiste typen worden vervangen.

      Uw programma's moeten sjablonen maken voor algemene functies die met verschillende typen werken. Met andere woorden, als u slechts één type met een functie gebruikt, hoeft u geen sjabloon toe te passen.

      Als een functie meerdere typen vereist, wijst de sjabloon eenvoudigweg aan elk type een unieke identifier toe, zoals T, T1 en T2. Later in het compilatieproces zal de C++-compiler de typen die u hebt opgegeven in het functieprototype correct toewijzen.

Sjablonenspecialisatie is een van de "complexe" kenmerken van de C++-taal en wordt voornamelijk gebruikt bij het maken van bibliotheken. Helaas worden sommige specifieke kenmerken van sjabloonspecialisatie niet goed behandeld in populaire boeken over deze taal. Bovendien beschrijven zelfs de 53 pagina's van de officiële ISO-taalstandaard die aan sjablonen zijn gewijd, interessante details in een rommelige vorm, waardoor er veel overblijft om "zelf te raden - het is duidelijk". Onder de snit heb ik geprobeerd de basisprincipes van sjabloonspecialisatie duidelijk te vermelden en te laten zien hoe deze principes kunnen worden gebruikt bij de constructie van magische spreuken.

Hallo Wereld

Hoe zijn we gewend om sjablonen te gebruiken? Gebruik het sjabloonsleutelwoord gevolgd door de namen tussen punthaken sjabloon parameters gevolgd door het type en de naam. Bij parameters geven ze ook aan wat het is: een type (typenaam) of een waarde (bijvoorbeeld int). Het type sjabloon zelf kan een klasse (klasse), structuur (struct is eigenlijk een klasse) of functie (bool foo () enzovoort) zijn. De eenvoudigste sjabloonklasse "A" kan bijvoorbeeld als volgt worden gedefinieerd:

Na een tijdje willen we dat onze klas voor alle typen hetzelfde werkt, maar anders voor iets lastigs als int. Bullshit-vraag, een specialisatie schrijven: het ziet er hetzelfde uit als een advertentie, maar parameters we geven de sjabloon niet tussen punthaken aan, in plaats daarvan geven we specifieke aan argumenten sjabloon achter de naam:

Sjabloon<>klasse A, eerste klasse< int >(); // int hier is het sjabloonargument
Als u klaar bent, kunt u methoden en velden van een speciale implementatie voor int schrijven. Deze specialisatie wordt meestal compleet(volledige specialisatie of expliciete specialisatie). Voor de meeste praktische taken is niet meer nodig. En indien nodig, dan...

Aangepaste sjabloon is een nieuwe sjabloon

Als u de ISO C ++-standaard aandachtig leest, kunt u een interessante verklaring vinden: door een gespecialiseerde sjabloonklasse te maken, creëren we nieuwe sjabloonklas(14.5.4.3). Wat levert het ons op? Een gespecialiseerde sjabloonklasse kan methoden, velden of typedeclaraties bevatten die niet voorkomen in de sjabloonklasse die we specialiseren. Het is handig wanneer u een sjabloonklasse-methode nodig hebt om alleen voor een specifieke specialisatie te werken - het is voldoende om de methode alleen in deze specialisatie te declareren, de compiler doet de rest:

Een gespecialiseerde sjabloon kan zijn eigen sjabloonparameters hebben

De duivel zit, zoals u weet, in de details. Het feit dat een gespecialiseerde sjabloonklasse een geheel nieuwe en aparte klasse is, is zeker interessant, maar er zit niet veel magie in. En de magie is in een onbeduidende consequentie - als het een afzonderlijke sjabloonklasse is, dan kan deze afzonderlijke hebben, op geen enkele manier verbonden met een niet-gespecialiseerde sjabloonklasse. parameters(parameters zijn wat na sjabloon tussen punthaken staat). Bijvoorbeeld als volgt:

Sjabloon< typename S, typename U >klasse A, eerste klasse< int > {};
Toegegeven, dit is precies de code van de compiler wil niet compileren- we gebruiken niet de nieuwe sjabloonparameters S en U, wat verboden is voor een gespecialiseerde sjabloonklasse (maar de gespecialiseerde compiler begrijpt dat dit een klasse is omdat deze dezelfde naam "A" heeft als de reeds gedeclareerde sjabloonklasse). De compiler zal zelfs een speciale fout zeggen: "expliciete specialisatie gebruikt gedeeltelijke specialisatiesyntaxis, gebruik sjabloon<>in plaats van ". Het geeft aan dat als er niets te zeggen is, je een sjabloon moet gebruiken<>en niet opscheppen. Waarom kunnen dan nieuwe parameters worden gebruikt in een gespecialiseerde sjabloonklasse? Het antwoord is vreemd - om te vragen argumenten specialisaties (argumenten zijn wat er achter de klassenaam tussen punthaken staat). Dat wil zeggen, door een sjabloonklasse te specialiseren, kunnen we, in plaats van een eenvoudige en begrijpelijke int, deze specialiseren via nieuwe parameters:

Sjabloon< typename S, typename U >klasse A, eerste klasse< std::map< S, U > > {};
Zo'n vreemde notatie zal compileren. En wanneer de resulterende sjabloonklasse wordt gebruikt met std :: map, wordt een specialisatie gebruikt, waarbij het sleuteltype std :: map beschikbaar zal zijn als een parameter van de nieuwe sjabloon S, en het std :: mapwaardetype als U.

Een dergelijke specialisatie van de sjabloon, waarin een nieuwe lijst met parameters wordt ingesteld en via deze parameters argumenten voor de specialisatie worden ingesteld, wordt genoemd gedeeltelijke specialisatie(deelspecialisatie). Waarom "gedeeltelijk"? Blijkbaar omdat het oorspronkelijk bedoeld was als een syntaxis voor het specialiseren van een sjabloon, niet voor alle argumenten. Een voorbeeld waarbij een sjabloonklasse met twee parameters zich in slechts één ervan specialiseert (specialisatie werkt wanneer het eerste argument, T, is opgegeven als int. In dit geval kan het tweede argument van alles zijn - hiervoor is een nieuwe parameter U geïntroduceerd in de deelspecialisatie en wordt gespecificeerd in de lijst met argumenten voor specialisatie):

Sjabloon< typename T, typename S >klasse B (); sjabloon< typename U >klasse B< int, U > {};

De magische gevolgen van gedeeltelijke specialisatie

Er zijn een aantal interessante implicaties van de twee eigenschappen van sjabloonspecialisatie die hierboven zijn beschreven. Als u bijvoorbeeld gedeeltelijke specialisatie gebruikt, kunt u, door nieuwe sjabloonparameters te introduceren en gespecialiseerde argumenten erdoor te beschrijven, samengestelde typen opsplitsen in eenvoudigste. In het onderstaande voorbeeld wordt de gespecialiseerde sjabloonklasse A gebruikt als de sjabloonargumenten van het type functieaanwijzer zijn. Tegelijkertijd kunt u via de nieuwe parameters van de sjabloon S en U het type van de geretourneerde waarde van deze functie en het type van zijn argument krijgen:

Sjabloon< typename S, typename U >klasse A, eerste klasse< S(*)(U) > {};
En als u typedef of static const int in een gespecialiseerde sjabloon declareert (gebruik makend van het feit dat dit een nieuwe sjabloon is), dan kunt u deze gebruiken om de benodigde informatie uit het type te halen. We gebruiken bijvoorbeeld een sjabloonklasse om objecten op te slaan en willen de grootte van het doorgegeven object krijgen, of 0 als het een aanwijzer is. In twee regels:

Sjabloon< typename T >struct Get (const static int Grootte = sizeof (T);); sjabloon< typename S >struct Get< S* >(const static int Grootte = 0;); Krijgen< int >:: Maat // bijv. 4 Get< int* >:: Grootte // 0 - vond de aanwijzer :)
Dit soort magie wordt voornamelijk gebruikt in bibliotheken: stl, boost, loki, enzovoort. Bij programmeren op hoog niveau is het gebruik van dergelijke trucs natuurlijk omslachtig - ik denk dat iedereen zich de constructie herinnert om de grootte van een array te krijgen :). Maar in bibliotheken maakt gedeeltelijke specialisatie het relatief eenvoudig om gedelegeerden, evenementen, complexe containers en andere soms zeer noodzakelijke en nuttige dingen te implementeren.

Collega's, als u een fout vindt (en ik ben helaas geen goeroe - ik kan het mis hebben) of u heeft kritiek, vragen of aanvullingen op het bovenstaande, dan zal ik graag reageren.

Update: Beloofd vervolg