Template
Templater er et verktøy i programmeringsspråket C++ som gjør det mulig for funksjoner og klasser til å operere med generiske typer. Klasser og funksjoner kan dermed jobbe med mange forskjellige datatyper uten at det må skrives en ny en for hver ny datatype.
Templater er et veldig kraftig verktøy i C++, særlig når det lar seg kombinere med multippel arv og operatoroverlastinger. Templater er turingkomplett, noe som ble oppdaget ved en tilfeldighet. Dette har åpnet opp for en teknikk relatert til metaprogrammering kalt templatmetaprogrammering som blant annet standardbiblioteket i C++ kjent som «The C++ standard library» har benyttet seg stort av.[1] Standardbiblioteket har mange funksjoner og klasser som er bygget som et nettverk av relaterte templater.
Teknisk oversikt
[rediger | rediger kilde]Det finnes tre forskjellige typer templater; funksjonstemplater, klassetemplater og variabeltemplater. Sistnevnte kom med versjonsstandarden C++14. Med C++11 kan templater enten være variadiske eller ikke-variadiske. I alle tidligere versjonsstandarder er de ikke-variadiske.
Funksjonstemplater
[rediger | rediger kilde]En funksjonstemplate fungerer som en normal funksjon sett bort i fra at den kan ha argumenter av mange forskjellige typer. Med andre ord representerer en funksjonstemplate en familie av funksjoner. Måten man deklarerer disse på er som følgende
template <class identifikator> funksjonsdeklarering;
template <typename identifikator> funksjonsdeklarering;
Begge de to linjene er ekvivalente. Linje nummer 2 ble introdusert for å unngå forvirring, siden en typeparameter ikke nødvendigvis trenger å være en klasse, men kan også være av en primitiv type som int eller double. For eksempel har standardbiblioteket i C++ funksjonstemplaten max(x, y) som returnerer hvilken av x og y som er størst. Funksjonstemplaten kan bli skrevet som følgende
template <typename Type>
Type max(Type a, Type b) {
return a > b ? a : b;
}
Denne ene funksjonstemplaten fungerer med mange datatyper. Den skal fungere med både primitive typer og alle klassetyper som har definert operatoren > og har definert en kopikonstruktør. Dette er fordi funksjonstemplaten er definert til å ta argumentene ved verdier, ikke referanser. Ved å bruke funksjonstemplater slipper vi å skrive mange forskjellige funksjonsdefinisjoner med forskjellige typer. Vi trenger dermed kun å vedlikeholde en funksjon som vil gjøre koden mer lesbar. En template vil uansett ikke lage mindre objektkode enn om man skulle definert separate funksjoner for hver type som skal brukes. Her er et eksempel på hvordan funksjonstemplaten max kan brukes.
#include <iostream>
int main() {
// Denne vil kalle på max<int> ved implisitt argumentdeduksjon
std::cout << max(3, 7) << std::endl;
// Denne vil kalle på max<int> uten implisitt argumentdeduksjon
std::cout << max<int>(3, 7) << std::endl;
return 0;
}
Klassetemplater
[rediger | rediger kilde]En klassetemplate vil generere klasser basert på typeparameterne. Klassetemplater blir ofte brukt for å skrive såkalte konteinere som lagrer andre objekter på en organisert måte som følger spesifike tilgangsregler. Standardbiblioteket i C++ har mange klassetemplater, et eksempel er std::vector.
Variabeltemplater
[rediger | rediger kilde]I C++14 kan templater også bli brukt for variabler, som i følgende eksempel som er en tilnærming til tallet pi, som kan representeres ved forskjellige typer, både brukerdefinerte og primitive.
template <typename T> constexpr T pi = T(3.141592653589793238462643L);
Variadiske templater
[rediger | rediger kilde]Med C++11 kom variadiske templater, templater som kan ta et ubegrenset antall typeargumenter. std::printf er et eksempel på en slik funksjon. Det ble også introdusert som et typesikkert alternativ for ellipser, som kom med arven fra C. Både funksjonstemplater og klassetemplater kan være variadiske.
Templatargumenter
[rediger | rediger kilde]Templatargumenter deles inn i tre typer; «non-type template arguments» (ikke-type templatargumenter), «type template arguments» (typetemplatargumenter), «template template arguments» (templattemplatargumenter) og «default template arguments» (standardtemplatargumenter).[2] Alle gitte templatargumenter må være konstantuttrykk ettersom templatargumentene må være kjent ved kompilering.
Ikke-type templatargumenter blir ofte brukt innen templatmetaprogrammering. Dette er vanlige typeargumenter som kan gis som konstantuttrykk, altså beregnbare under kompilering. Det omfatter blant annet heltallstyper, pekere, referanser og enums.
Typetemplatargumenter brukes gjerne for generisk programmering. Typeparameteren står for en ukjent type som substitueres for typeargumentet når templaten instansieres med en type. Alle tidligere nevnte eksempler har utelukkende hatt typetemplatargumenter.
Templattemplatargumenter er templatargumenter som selv tar en template som argument. Det brukes ofte for generiske operasjoner på konteinere.
Standardtemplatargumenter kan tenkes på som typetemplatparametere med en gitt standardtype. Om templaten instansieres uten å spesifisere templatargumentet, brukes den gitte standardtypen i stedet. En analogi kan være C++ sine standardverdier for funksjonsargumenter. Det er det samme prinsippet i dette tilfellet, bortsett fra at vi opererer med templater og dermed med typer.
Templatspesialisering
[rediger | rediger kilde]Utdypende artikkel: Templatspesialisering
Sammenlignet med makroer
[rediger | rediger kilde]Templater ble introdusert som et typesikkert alternativ til makroer. Makroer har mange problemer knyttet til at tolkningen foregår i preprosessoren. Preprosessoren modifiserer kildekoden før den sendes videre til kompilatoren. Makroekspansjonene gjøres dermed uavhengig av kompilatoren, noe som ofte leder til feilmeldinger som kan være vanskelig å tolke. Templater forsøker å løse dette problemet. Det blir tolket av kompilatoren, dermed unngår man mange av fallgruvene ved makroer.
Sammenlignet med generisk programmering
[rediger | rediger kilde]Selv om C++ sitt templatsystem, Java generics og .NET generics ligner på hverandre, er det fortsatt viktige forskjeller. Underliggende fungerer en template slik at den representeres som en familie av funksjoner eller klasser som blir generert basert på typeargumentene når programmet kjøres. Dette representeres i kildekoden som en template. Dette er til forskjell fra generiske teknikker i programmeringsspråk. I generics vil kun de mest grunnleggende karakteristikkene ved C++ sitt templatsystem etterlignes. Java generics har type erasure som ofte er typekastinger som kompilatoren selv legger inn, dermed vil ikke templatargumentene være tilgjengelig under kjøretid. Det har heller ikke mulighet til å ta annet enn klasser som typeargumenter. Enkelte avanserte teknikker ved templater i C++ som blir brukt i blant annet standardbiblioteket, eksisterer ikke i generics. Eksempler på dette er eksplisitt eller partiell spesialisering av templater, standardargumenter og såkalte «non-type» (ikke-type) templatargumenter, og template template argumenter. Ingen av disse eksisterer i for eksempel Java generics, dermed har man ikke mulighet for blant annet templatmetaprogrammering.
Se også
[rediger | rediger kilde]Referanser
[rediger | rediger kilde]- ^ Meyers, Scott (2005). «7». Effective C++. Addison-Wesley Professional; 3 edition. s. 233. ISBN 0-321-33487-6.
- ^ «Template parameters and template arguments - cppreference.com». en.cppreference.com. Besøkt 9. august 2016.