C++ šablony

Aktuální verze stránky ještě nebyla zkontrolována zkušenými přispěvateli a může se výrazně lišit od verze recenzované 30. března 2016; kontroly vyžadují 29 úprav .

Š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.

Šablony funkcí

Popis šablony Syntaxe

Š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.

Příklad použití

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" ) );

Volání funkce šablony

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 v šablonách

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 );

Šablony tříd

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 ); /* ... */ };

Použití šablon

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!" );

Technické detaily

Možnosti šablony

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 šablony

Pokud 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.

Pravidla pro odvozování argumentů šablony funkcí

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 );

Členové tříd šablon

Č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říd

Pokud 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říd

Existují 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 }

Kritika a srovnání s alternativami

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] .

Viz také

Poznámky

  1. Standard C++ „Standard pro programovací jazyk C++“: ISO/IEC 14882 1998 .
  2. K. Czarnecki, J. O'Donnell, J. Striegnitz, W. Taha. Implementace DSL v metaocaml, šabloně haskell a C++ . — University of Waterloo, University of Glasgow, Research Center Julich, Rice University, 2004. .
    Citace: C++ Template Metaprogramming trpí řadou omezení, včetně problémů s přenositelností způsobených omezeními kompilátoru (ačkoli se to v posledních několika letech výrazně zlepšilo), nedostatkem podpory ladění nebo IO během vytváření šablony, dlouhými časy kompilace, dlouhými a nepochopitelnými chybami. , špatná čitelnost kódu a špatné hlášení chyb.
  3. Sheard T., Jones SP Template Metaprogramming for Haskell  // Haskell Workshop. - Pittsburgh: ACM 1-58113-415-0/01/0009, 2002 .
    Citát z Robinsonova provokativního článku označuje šablony C++ za hlavní, byť náhodný úspěch návrhu jazyka C++. Navzdory extrémně barokní povaze metaprogramování šablon se šablony používají fascinujícím způsobem, který přesahuje ty nejdivočejší sny jazykových návrhářů. Možná překvapivě, vzhledem k tomu, že šablony jsou funkční programy, funkcionální programátoři jen pomalu těžili z úspěchu C++.
  4. ↑ Digital Mars : D Programming Language 2.0  

Literatura

  • David Vandevoerd, Nicholas M. Josattis. C ++ Templates: The Complete Guide = C++ Templates: The Complete Guide. - M .: "Williams" , 2003. - S.  544 . — ISBN 0-201-73484-2 .
  • Podbelský V. V. 6.9. Šablony funkcí //Kapitola 6. Funkce, ukazatele, odkazy // Jazyk C++ / recenze. Dadaev Yu. G. - 4. - M. : Finance and Statistics , 2003. - S. 230-236. — 560 str. - ISBN 5-279-02204-7 , MDT 004.438Si (075.8) LBC 32.973.26-018 1ya173.

Odkazy