A Templates ( eng. template ) egy C ++ nyelvi eszköz, amelyet általánosított algoritmusok kódolására terveztek , anélkül, hogy bizonyos paraméterekhez (például adattípusokhoz , pufferméretekhez, alapértelmezett értékekhez) kötődnének.
A C++ nyelven lehetőség van függvény- és osztálysablonok létrehozására .
A sablonok lehetővé teszik paraméterezett osztályok és függvények létrehozását. A paraméter lehet bármilyen típusú, vagy az engedélyezett típusok egyikének értéke (egész szám, enum, bármely, globálisan elérhető nevű objektumra mutató mutató, hivatkozás). Például szükségünk van egy osztályra:
class SomeClass { int SomeValue ; int SomeArray [ 20 ]; ... };Egy konkrét célra használhatjuk ezt az osztályt. De hirtelen egy kicsit megváltozott a cél, és egy másik osztályra van szükség. Most 30 tömbelemre SomeArrayés egy valós SomeValueelemtípusra van szükségünk SomeArray. Ekkor elvonatkoztathatunk a konkrét típusoktól, és paraméteres sablonokat használhatunk. Szintaxis: az elején, az osztály deklarálása előtt deklaráljuk a sablont, azaz templateszögletes zárójelben adjuk meg a paramétereket. Példánkban:
template < int ArrayLength , typename SomeValueType > class SomeClass { SomeValueType SomeValue ; SomeValueType SomeArray [ ArrayLength ]; ... };Ezután az első esetre (20 elemből álló SomeValue és SomeArray egész számmal) ezt írjuk:
SomeClass < 20 , int > SomeVariable ;a másodikhoz:
SomeClass < 30 , double > SomeVariable2 ;Bár a sablonok rövidítést biztosítanak egy kódrészlethez, használatuk valójában nem rövidíti le a végrehajtható kódot, mivel a fordító minden egyes beállításkészlethez külön példányt hoz létre egy függvényből vagy osztályból. Ennek eredményeként megszűnik a lefordított kód megosztott könyvtárakon belüli megosztásának lehetősége.
A függvénysablon a kulcsszóval kezdődik, templateamit a paraméterek listája követ szögletes zárójelben. Ezután jön a függvény deklarációja:
template < typename T > void sort ( T array [], int size ); // prototípus: a rendezési sablon deklarálva van, de nincs definiálva sablon < típusnévT > _ void sort ( T array [], int size ) // deklaráció és definíció { T t ; for ( int i = 0 ; i < méret - 1 ; i ++ ) for ( int j = méret - 1 ; j > i ; j -- ) if ( tömb [ j ] < tömb [ j -1 ]) { t = tömb [ j ]; tömb [ j ] = tömb [ j -1 ]; tömb [ j -1 ] = t ; } } template < int BufferSize > // integer paraméter char * read () { char * Buffer = új karakter [ BufferSize ]; /* adat olvasása */ return Buffer ; }A kulcsszó typenameviszonylag friss, így a szabvány [1] lehetővé teszi a használatát a classhelyett typename:
sablon < classT > _A T helyett bármilyen más azonosító elfogadható.
A legegyszerűbb példa a minimum két mennyiség meghatározása.
Ha a kisebb, mint b, akkor adja vissza a-t, ellenkező esetben b-t
Sablonok hiányában a programozónak külön függvényeket kell írnia minden egyes adattípushoz. Bár sok programozási nyelv definiál egy beépített minimális függvényt az elemi típusokhoz (például egész és valós számokhoz), egy ilyen függvényre szükség lehet összetett (például „idő” vagy „karakterlánc”) és nagyon összetett (“ játékos” egy online játékban ) objektumok .
Így néz ki a minimális függvénysablon:
sablon < típusnévT > _ T min ( T a , T b ) { visszatér a < b ? a : b ; }A függvény meghívásához egyszerűen használja a nevét:
min ( 1 , 2 ); min ( 'a' , 'b' ); min ( string ( "abc" ), string ( "cde" ) );Általánosságban elmondható, hogy egy sablonfüggvény meghívásához meg kell adnia az összes sablonparaméter értékeit. Ehhez a sablon neve után a szögletes zárójelben szereplő értékek listája látható:
int i [] = { 5 , 4 , 3 , 2 , 1 }; sort < int > ( i , 5 ); char c [] = "bvgda" ; sort < char > ( c , strlen ( c ) ); sort < int > ( c , 5 ); // hiba: a sort<int> int[] paraméterrel rendelkezik, nem char[] char * ReadString = olvasás < 20 > (); törlés [] ReadString ; ReadString = olvasás < 30 > ();A fordító minden beállításkészlethez létrehozza a függvény új példányát. Az új példány létrehozásának folyamatát sablonpéldányosításnak nevezzük .
A fenti példában a fordító két funkciósablon-specializációt hozott létre (a és sorttípusokhoz ) és két sablon-specializációt (a 20-as és 30 -as értékekhez ). Ez utóbbi nagy valószínűséggel pazarló, mivel a paraméter minden lehetséges értékéhez a fordító egyre több új függvénypéldányt hoz létre, amelyek csak egy konstansban különböznek egymástól. charintreadBufferSize
A paraméterértékek származtatásaEgyes esetekben a fordító egy függvényargumentumból következtethet (logikailag meghatározhatja) egy függvénysablon paraméter értékét. Például a fent leírt függvény meghívásakor sortnem szükséges megadni a sablon paramétert (ha megegyezik a tömb argumentum elemeinek típusával):
int i [ 5 ] = { 5 , 4 , 3 , 2 , 1 }; rendezés ( i , 5 ); // hívás sort<int> char c [] = "bvgda" ; sort ( c , strlen ( c ) ); // call sort<char>Bonyolultabb esetekben is lehetséges az eltávolítás .
Egész paraméterekkel rendelkező osztálysablonok használata esetén ezekre a paraméterekre is lehet következtetni. Például:
sablon < int méret > osztály IntegerArray { int Tömb [ méret ]; /* ... */ }; sablon < int méret > // Sablon prototípus void PrintArray ( IntegerArray < méret > tömb ) { /* ... */ } // Sablonhívás // A sablonobjektum használata IntegerArray < 20 > ia ; PrintArray ( ia );Következtetési szabályokat vezetnek be a nyelvbe, hogy megkönnyítsék a sablonok használatát, és elkerüljék az esetleges hibákat, mint például sort< int >a karaktertömb rendezése.
Ha egy sablonparaméter több argumentumból is kikövetkeztethető, akkor a következtetés eredményének pontosan ugyanannak kell lennie az összes argumentum esetében. Például a következő hívások hibásak:
min ( 0 , 'a' ); min ( 7 , 7,0 );Az adott sablonparaméterek használatával kapcsolatos hibák nem észlelhetők a sablon használata előtt. Például minmaga a sablon nem tartalmaz hibákat, de ha olyan típusokkal használja, amelyekhez a művelet '<'nincs definiálva, az hibát eredményez:
struktúra A { int a ; }; A obj1 , obj2 ; min ( obj1 , obj2 );Ha '<'a sablon első használata előtt adja meg a műveletet, akkor a hiba megszűnik. Így nyilvánul meg a sablonok rugalmassága a C++ nyelvben :
barát inline bool operátor < ( const A & a1 , const A & a2 ) { return a1 . a < a2 . a ; } min ( obj1 , obj2 );Az egész számok összekapcsolt listáját megvalósító osztályban az új elem listához való hozzáadásának és a kívánt elem keresésének algoritmusa nem függ attól, hogy a lista elemei egész számok. Ugyanezek az algoritmusok vonatkoznának a karakterek, karakterláncok, dátumok, játékososztályok stb. listájára.
sablon < classT > _ osztály listája { /* ... */ nyilvános : void Add ( const T & Element ); bool Find ( const T & Element ); /* ... */ };Az osztálysablon használatához meg kell adnia a paramétereit:
Lista < int > li ; Lista < string > ls ; li . hozzá ( 17 ); ls . Add ( "Helló!" );A sablon paraméterei lehetnek: típusparaméterek, normál típusparaméterek, sablonparaméterek.
Bármilyen típusú paraméterhez megadhat alapértelmezett értékeket.
template < class T1 , // típusparaméter típusnév T2 , // paraméter típusa int I , // szabályos típusú paraméter T1 DefaultValue , // szabályos típusú paraméter template < class > class T3 , // sablon paraméter osztály Karakter = char // alapértelmezett paraméter > Sablon paramétereiHa egy osztályban vagy függvénysablonban ugyanazt a sablont kell használni, de eltérő paraméterekkel, akkor sablonparamétereket használunk. Például:
sablon < osztály Típus , sablon < osztály > osztály Tároló > osztályú kereszthivatkozások { Container < Type > mems ; Container < Type * > refs ; /* ... */ }; CrossReferences < Dátum , vektor > cr1 ; CrossReferences < string , set > cr2 ;A függvénysablonok nem használhatók sablonparaméterként.
Típusú paraméterek esetén (például a rendezési függvény T paramétere) akkor lehetséges a következtetés, ha a függvény argumentuma a következő típusok valamelyikébe tartozik:
Az érv típusa | Leírás |
---|---|
T const T volatile T |
Maga a típus T, esetleg módosítókkal constvagy volatile. sablon < classT > _ T ReturnMe ( const T arg ) { return arg ; } ReturnMe ( 7 ); ReturnMe ( 'a' ); |
T* T& T[A] A egy állandó |
típusú mutató, hivatkozás vagy elemek tömbje T.
Példa erre a fentebb tárgyalt rendezési függvény sablon. |
Templ<T> Templ – osztálysablon neve |
Érvként a függvény valamilyen sablon speciális specializációját igényli. #include <vektor> sablon < classT > _ void rendezés ( vektor < T > tömb ) { /* rendezés */ } vektor < int > i ; vektor < char > c ; rendezés ( i ); rendezés ( c ); |
T (*) (args) args – néhány érv |
Mutasson egy függvényre, amely T típust ad vissza. sablon < classT > _ T * CreateArray ( T ( * GetValue )(), const int size ) { T * Tömb = új T [ méret ]; for ( int i = 0 ; i < méret ; i ++ ) Tömb [ i ] = GetValue (); return Array ; } int GetZero () { return 0 ; } char InputChar () { char c ; cin >> c ; visszatér c ; } int * ArrayOfZeros = CreateArray ( GetZero , 20 ); char * String = CreateArray ( InputChar , 40 ); |
type T::* T Class::* típus - néhány típus Osztály - néhány osztály |
A T osztály tetszőleges típusú tagjára mutató mutató. Mutasson egy tetszőleges osztály T típusú tagjára. osztály MyClass { nyilvános : int a ; }; sablon < classT > _ T & IncrementIntegerElement ( int T ::* elem , T & objektum ) { tárgyat . * Elem += 1 ; return Object ; } sablon < classT > _ T IncrementMyClassElement ( T MyClass ::* Elem , MyClass & Object ) { tárgyat . * Elem += 1 ; objektum visszaadása . * Elem ; } MyClass Obj ; int n ; n = ( IncrementIntegerElement ( & MyClass :: a , Obj ) ). a ; n = IncrementMyClassElement ( & MyClass :: a , Obj ); |
type (T::*) (args) T (Class::*) (args) típus - néhány típus Osztály - néhány osztály args - néhány argumentum |
Mutató a T osztály tetszőleges típusú tagfüggvényére. Mutasson egy tetszőleges osztály T típusú tagfüggvényére. osztály MyClass { nyilvános : int a ; int NövekményA (); }; int MyClass::IncrementA () { return ++ a ; } sablon < classT > _ T & CallIntFunction ( int ( T ::* függvény )(), T & Object ) { ( Objektum . * Funkció )(); return Object ; } sablon < classT > _ T CallMyClassFunction ( T ( MyClass ::* függvény )(), MyClass & Object ) { return ( Object . * Function )(); } MyClass Obj ; int n ; n = ( CallIntFunction ( & MyClass :: IncrementA , Obj ) ). a ; n = CallMyClassFunction ( & MyClass :: IncrementA , Obj ); |
Az osztálysablon tagjai sablonok, és ugyanazzal a paraméterezéssel rendelkeznek, mint az osztálysablon. Ez különösen azt jelenti, hogy a tagfüggvények meghatározását a sablon fejlécével kell kezdeni:
sablon < classT > _ A osztály { void f ( T adat ); void g ( érvénytelen ); nyilvános : A (); }; sablon < classT > _ void A < T >:: f ( T adat ); sablon < classT > _ void A < T >:: g ( érvénytelen );A sablon hatókörén belül a specifikációt nem kell megismételni. Ez azt jelenti, hogy például A<T>::A() egy konstruktor , bár írhatsz A<T>::A<T>().
Típusok osztályok tagjaikéntHa a sablon paramétere egy olyan osztály, amelynek adattípusú tagja van , akkor a kulcsszót kell használni az adott tag használatához typename. Például:
osztályú konténer { nyilvános : int tömb [ 15 ]; typedef int * iterátor ; /* ... */ iterator begin () { return array ; } }; sablon < C osztály > void f ( C és vektor ) { C :: iterátor i = vektor . kezdődik (); // hibatípusnév C :: iterátor i = vektor . kezdődik (); } Sablonok osztályok tagjaikéntProblémák vannak a sablontagokkal is. Ha ebben a sablonban (f) használunk egy sablont (ConvertTo()), amely egy (A) osztály tagja, amely viszont egy sablonparaméter (f), és nem teszi lehetővé a paraméterek következtetését, akkor a minősítő használni kell template:
A osztály { /* ... */ nyilvános : sablon < class T > T & ConvertTo (); template < class T > void ConvertFrom ( const T & data ); }; sablon < classT > _ void f ( T Container ) { int i1 = Tároló . sablon ConvertTo < int > () + 1 ; konténer . ConvertFrom ( i1 ); // nem szükséges minősítő }A C++ sablon metaprogramozása számos korláttól szenved, beleértve a hordozhatósági problémákat, a hibakeresés vagy az I/O támogatás hiányát a sablon példányosítása során, a hosszú fordítási időt, a rossz kód olvashatóságot, a rossz hibadiagnosztikát és a homályos hibaüzeneteket [2] . A C++ sablon alrendszert Turing-komplett, tisztán funkcionális programozási nyelvként határozzák meg, de a funkcionális stílusú programozók ezt provokációnak tartják, és nem szívesen ismerik el a C++-t sikeres nyelvként [3] .
Sok nyelv ( Java 5, Ada , Delphi 2009) egyszerűbb módon valósítja meg az általános programozási támogatást , néhányan akár típusrendszer szinten is (lásd Eiffel és parametrikus polimorfizmus az ML nyelvcsaládban ); az ilyen nyelveknek nincs szükségük a C++ sablonokhoz hasonló mechanizmusokra.
A C makró helyettesítési lehetőségei, bár nem Turing teljesek, elegendőek az alacsony szintű programozáshoz a generatív programozásban , és képességeiket jelentősen bővítették a C99 -ben.
A D nyelvnek vannak olyan sablonjai, amelyek erősebbek, mint a C++. [4] .
Adattípusok | |
---|---|
Értelmezhetetlen | |
Numerikus | |
Szöveg | |
Referencia | |
Összetett | |
absztrakt | |
Egyéb | |
Kapcsolódó témák |