Šablony ( angl. template ) - jazykový nástroj C++ určený pro kódování zobecněných algoritmů , aniž by byl vázán na některé parametry (například datové typy , velikosti vyrovnávací paměti, výchozí hodnoty).
V C++ je možné vytvářet šablony funkcí a tříd .
Šablony umožňují vytvářet parametrizované třídy a funkce. Parametrem může být libovolný typ nebo hodnota jednoho z povolených typů (celé číslo, enum, ukazatel na libovolný objekt s globálně přístupným názvem, odkaz). Potřebujeme například třídu:
třída SomeClass { int SomeValue ; int SomeArray [ 20 ]; ... };Pro jeden konkrétní účel můžeme použít tuto třídu. Ale najednou se cíl trochu změnil a je potřeba další třída. Nyní potřebujeme 30 prvků pole SomeArraya skutečný typ SomeValueprvku SomeArray. Pak můžeme abstrahovat od konkrétních typů a používat šablony s parametry. Syntaxe: na začátku, před deklarací třídy, deklarujeme šablonu, templatetedy specifikujeme parametry v lomených závorkách. V našem příkladu:
template < int ArrayLength , typename SomeValueType > class SomeClass { SomeValueType SomeValue ; SomeValueType SomeArray [ ArrayLength ]; ... };Pak pro první případ (s celým číslem SomeValue a SomeArray o 20 prvcích) zapíšeme:
SomeClass < 20 , int > SomeVariable ;za druhé:
SomeClass < 30 , double > SomeVariable2 ;Ačkoli šablony poskytují zkratku pro kus kódu, jejich použití ve skutečnosti nezkracuje spustitelný kód, protože kompilátor vytváří samostatnou instanci funkce nebo třídy pro každou sadu voleb. V důsledku toho mizí možnost sdílet kompilovaný kód v rámci sdílených knihoven.
Šablona funkce začíná klíčovým slovem templatenásledovaným seznamem parametrů v lomených závorkách. Pak přichází deklarace funkce:
template < typename T > void sort ( T pole [], int size ); // prototyp: šablona řazení je deklarována, ale není definována šablona < typenameT > _ void sort ( T pole [], int size ) // deklarace a definice { Tt ; _ for ( int i = 0 ; i < velikost - 1 ; i ++ ) for ( int j = velikost - 1 ; j > i ; j -- ) if ( pole [ j ] < pole [ j -1 ]) { t = pole [ j ]; pole [ j ] = pole [ j -1 ]; pole [ j - 1 ] = t ; } } template < int BufferSize > // parametr celého čísla char * čtení () { char * Buffer = nový znak [ BufferSize ]; /* číst data */ návratová vyrovnávací paměť ; }Klíčové slovo je typenamerelativně nedávné, takže standard [1] umožňuje použít classmísto typename:
šablona < classT > _Místo T je přijatelný jakýkoli jiný identifikátor.
Nejjednodušším příkladem je stanovení minima dvou veličin.
Je-li a menší než b, vraťte a, jinak vraťte b
Při absenci šablon musí programátor napsat samostatné funkce pro každý použitý datový typ. Ačkoli mnoho programovacích jazyků definuje vestavěnou minimální funkci pro elementární typy (jako jsou celá a reálná čísla), taková funkce může být potřebná pro složité (například „čas“ nebo „řetězec“) a velmi složité („“ hráč“ v online hře ) objekty .
Takto vypadá šablona minimální funkce:
šablona < typenameT > _ T min ( T a , T b ) { vrátit a < b ? a : b ; }K volání této funkce můžete jednoduše použít její název:
min ( 1,2 ) ; _ min ( 'a' , 'b' ); min ( řetězec ( "abc" ), řetězec ( "cde" ) );Obecně řečeno, chcete-li volat funkci šablony, musíte zadat hodnoty pro všechny parametry šablony. Za tímto účelem je za názvem šablony uveden seznam hodnot v lomených závorkách:
int i [] = { 5 , 4 , 3 , 2 , 1 }; sort < int > ( i , 5 ); char c [] = "bvgda" ; sort < char > ( c , strlen ( c ) ); sort < int > ( c , 5 ); // chyba: sort<int> má parametr int[], nikoli char[] char * ReadString = čtení < 20 > (); delete [] ReadString ; ReadString = čtení < 30 > ();Pro každou sadu možností kompilátor vygeneruje novou instanci funkce. Proces vytváření nové instance se nazývá konkretizace šablony .
Ve výše uvedeném příkladu kompilátor vytvořil dvě specializace šablon funkcí sort(pro typy chara int) a dvě specializace šablon read(pro hodnoty BufferSize20 a 30). To druhé je s největší pravděpodobností plýtvání, protože pro každou možnou hodnotu parametru bude kompilátor vytvářet stále více nových instancí funkcí, které se budou lišit pouze o jednu konstantu.
Odvození hodnot parametrůV některých případech může kompilátor odvodit (logicky určit) hodnotu parametru šablony funkce z argumentu funkce. Například při volání funkce popsané výše sortnení nutné zadávat parametr šablony (pokud odpovídá typu prvků argumentu pole):
int i [ 5 ] = { 5 , 4 , 3 , 2 , 1 }; řazení ( i , 5 ); // volání sort<int> char c [] = "bvgda" ; sort ( c , strlen ( c ) ); // volání řazení<znak>Ve složitějších případech je možné i odstranění .
V případě použití šablon tříd s celočíselnými parametry je možné tyto parametry také odvodit. Například:
šablona < velikost int > třídy IntegerArray { int Pole [ velikost ]; /* ... */ }; šablona < velikost int > // Prototyp šablony void PrintArray ( IntegerArray < velikost > pole ) { /* ... */ } // Volání šablony // Použití objektu šablony IntegerArray < 20 > ia ; PrintArray ( ia );Odvozovací pravidla jsou do jazyka zavedena, aby bylo snazší používat šablonu a aby se zabránilo možným chybám, jako je pokus sort< int >o seřazení pole znaků.
Pokud lze parametr šablony odvodit z více argumentů, musí být výsledek odvození přesně stejný pro všechny tyto argumenty. Například následující volání jsou chybná:
min ( 0 , 'a' ); min ( 7 , 7,0 );Chyby spojené s použitím konkrétních parametrů šablony nelze zjistit před použitím šablony. Například šablona minsama o sobě neobsahuje chyby, ale její použití s typy, pro které není operace '<'definována, povede k chybě:
struktura A { int a ; }; A obj1 , obj2 ; min ( obj1 , obj2 );Pokud zadáte operaci '<'před prvním použitím šablony, chyba bude odstraněna. Takto se projevuje flexibilita šablon v C++ :
friend inline bool operátor < ( const A & a1 , const A & a2 ) { return a1 . a < a2 . a ; } min ( obj1 , obj2 );Ve třídě, která implementuje propojený seznam celých čísel, algoritmy pro přidání nového prvku do seznamu a hledání požadovaného prvku nezávisí na skutečnosti, že prvky seznamu jsou celá čísla. Stejné algoritmy by se vztahovaly na seznam znaků, řetězců, dat, tříd hráčů a tak dále.
šablona < classT > _ třída Seznam { /* ... */ veřejnost : void Přidat ( const T & Element ); bool Najít ( const T & Element ); /* ... */ };Chcete-li použít šablonu třídy, musíte zadat její parametry:
Seznam < int > li ; List < string > ls ; li _ přidat ( 17 ); ls . Přidat ( "Ahoj!" );Parametry šablony mohou být: parametry typu, parametry běžného typu, parametry šablony.
Pro parametry libovolného typu můžete zadat výchozí hodnoty.
šablona < class T1 , // parametr typu typename T2 , // parametr typu int I , // parametr běžného typu T1 DefaultValue , // šablona parametru běžného typu < class > class T3 , // třída parametru šablony Character = char // default parametr > Parametry šablonyPokud je nutné použít stejnou šablonu v šabloně třídy nebo funkce, ale s jinými parametry, použijí se parametry šablony. Například:
šablona < class Type , template < class > class Container > třídy křížové odkazy { Kontejner < Typ > mems ; Kontejner < Typ * > refs ; /* ... */ }; Křížové odkazy < Datum , vektor > cr1 ; Křížové odkazy < string , set > cr2 ;Šablony funkcí nelze použít jako parametry šablony.
Pro parametry, které jsou typy (například parametr T funkce řazení), je možné odvodit, pokud je argument funkce jednoho z následujících typů:
Typ argumentu | Popis |
---|---|
T const T volatile T |
Samotný typ T, případně s modifikátory constnebo volatile. šablona < classT > _ T ReturnMe ( const T arg ) { return arg ; } ReturnMe ( 7 ); ReturnMe ( 'a' ); |
T* T& T[A] A je konstanta |
Ukazatel, odkaz nebo pole prvků typu T.
Příkladem je výše popsaná šablona funkce řazení. |
Templ<T> Templ - název šablony třídy |
Jako argument funkce vyžaduje specifickou specializaci nějaké šablony. #include <vektor> šablona < classT > _ void sort ( vector < T > array ) { /* sort */ } vektor < int > i ; vektor < char > c ; seřadit ( i ); třídit ( c ); |
T (*) (args) args - některé argumenty |
Ukazatel na funkci, která vrací typ T. šablona < classT > _ T * CreateArray ( T ( * GetValue )(), const int size ) { T * Pole = nové T [ velikost ]; for ( int i = 0 ; i < velikost ; i ++ ) Pole [ i ] = GetValue (); return Array ; } int GetZero () { return 0 ; } char InputChar () { char c ; cin >> c ; návrat c ; } int * ArrayOfZeros = CreateArray ( GetZero , 20 ); char * String = CreateArray ( InputChar , 40 ); |
type T::* T Class::* typ - nějaký typ Třída - nějaká třída |
Ukazatel na člen třídy T libovolného typu. Ukazatel na člen typu T libovolné třídy. třída MyClass { veřejnost : int a ; }; šablona < classT > _ T & IncrementIntegerElement ( int T ::* Element , T & Object ) { objekt . * prvek += 1 ; return Object ; } šablona < classT > _ T IncrementMyClassElement ( T MyClass ::* Element , MyClass & Object ) { objekt . * prvek += 1 ; vrátit 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ějaký typ Třída - nějaká třída args - nějaké argumenty |
Ukazatel na členskou funkci třídy T libovolného typu. Ukazatel na členskou funkci typu T libovolné třídy. třída MyClass { veřejnost : int a ; int PřírůstekA (); }; int MyClass::IncrementA () { return ++ a ; } šablona < classT > _ T & CallIntFunction ( int ( T ::* Funkce )(), T & Objekt ) { ( Objekt . * Funkce )(); return Object ; } šablona < classT > _ T CallMyClassFunction ( T ( MyClass ::* Function )(), MyClass & Object ) { return ( Object . * Function )(); } MyClass Obj ; int n ; n = ( CallIntFunction ( & MyClass :: IncrementA , Obj ) ). a ; n = CallMyClassFunction ( & MyClass :: IncrementA , Obj ); |
Členy šablony třídy jsou šablony a mají stejnou parametrizaci jako šablona třídy. Konkrétně to znamená, že definice členských funkcí by měla začínat záhlavím šablony:
šablona < classT > _ třída A { void f ( T data ); void g ( void ); veřejnost : A (); }; šablona < classT > _ void A < T >:: f ( T data ); šablona < classT > _ void A < T >:: g ( void );V rámci rozsahu šablony se specifikátor nemusí opakovat. To znamená, že například A<T>::A() , je konstruktor , i když můžete také psát A<T>::A<T>().
Typy jako členové třídPokud je parametrem šablony třída, která má člena datového typu , je třeba pro použití tohoto člena použít klíčové slovo typename. Například:
třída kontejner { veřejnost : pole int [ 15 ]; typedef int * iterátor ; /* ... */ iterator begin () { return pole ; } }; šablona < třída C > void f ( C & vector ) { C :: iterátor i = vektor . začít (); // error typename C :: iterator i = vector . začít (); } Šablony jako členové třídExistují také problémy s členy šablony. Pokud je v této šabloně (f) použita šablona (ConvertTo()), která je členem třídy (A), která je zase parametrem šablony (f) a neumožňuje odvodit parametry, pak kvalifikátor musí být použito template:
třída A { /* ... */ veřejnost : šablona < class T > T & ConvertTo (); šablona < class T > void ConvertFrom ( const T & data ); }; šablona < classT > _ void f ( kontejner T ) { int i1 = Kontejner . šablona ConvertTo < int > () + 1 ; kontejner . ConvertFrom ( i1 ); // není potřeba žádný kvalifikátor }Metaprogramování šablon v C++ trpí mnoha omezeními, včetně problémů s přenositelností, chybějící podpory ladění nebo I/O během vytváření instance šablony, dlouhé doby kompilace, špatná čitelnost kódu, špatná diagnostika chyb a nejasné chybové zprávy [2] . Šablonový subsystém C++ je definován jako Turingově kompletní čistý funkcionální programovací jazyk, ale programátoři funkcionálního stylu to považují za provokaci a zdráhají se uznat C++ jako úspěšný jazyk [3] .
Mnoho jazyků ( Java 5, Ada , Delphi 2009) implementuje podporu obecného programování jednodušším způsobem, některé dokonce na úrovni typového systému (viz Eiffel a parametrický polymorfismus v rodině jazyků ML ); takové jazyky nepotřebují mechanismy podobné šablonám C++.
Funkce makro substituce jazyka C, i když nejsou kompletní, jsou dostatečné pro nízkoúrovňové programování v generativním programování a jejich možnosti byly v C99 výrazně rozšířeny.
Jazyk D má šablony, které jsou výkonnější než C++. [4] .
Typy dat | |
---|---|
Neinterpretovatelné | |
Numerický | |
Text | |
Odkaz | |
Kompozitní | |
abstraktní | |
jiný | |
související témata |