C++ sablonok

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2016. március 30-án felülvizsgált verziótól ; az ellenőrzések 29 szerkesztést igényelnek .

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.

Függvénysablonok

Sablon leírása Szintaxis

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

Használati példa

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

Sablon függvényhívás

Á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ása

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

Hibák a sablonokban

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

Osztálysablonok

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

Sablonok használata

Az osztálysablon használatához meg kell adnia a paramétereit:

Lista < int > li ; Lista < string > ls ; li . hozzá ( 17 ); ls . Add ( "Helló!" );

Technikai részletek

Sablonbeállítások

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éterei

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

Szabályok a függvénysablon argumentumainak következtetéséhez

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

A sablonosztályok tagjai

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ént

Ha 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ént

Problé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ő }

Kritika és összehasonlítás az alternatívákkal

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

Lásd még

Jegyzetek

  1. C++ szabvány "A C++ programozási nyelv szabványa": ISO/IEC 14882 1998 .
  2. K. Czarnecki, J. O'Donnell, J. Striegnitz, W. Taha. DSL megvalósítás a metaocaml-ban, a haskell sablonban és a C++-ban . — Waterloo Egyetem, Glasgow Egyetem, Julich Kutatóközpont, Rice Egyetem, 2004 .
    Idézet: A C++ sablon metaprogramozása számos korláttól szenved, beleértve a fordítói korlátok miatti hordozhatósági problémákat (bár ez jelentősen javult az elmúlt néhány évben), a hibakeresési támogatás vagy az IO hiánya a sablon példányosítása során, hosszú fordítási idő, hosszú és érthetetlen hibák , a kód rossz olvashatósága és rossz hibajelentés.
  3. Sheard T., Jones SP Template Metaprograming for Haskell  // Haskell Workshop. - Pittsburgh: ACM 1-58113-415-0/01/0009, 2002. .
    Robinson provokatív cikkéből származó idézet a C++ sablonokat a C++ nyelvi tervezés jelentős, bár véletlenszerű sikereként azonosítja. A sablon meta-programozás rendkívül barokk jellege ellenére a sablonokat lenyűgöző módon használják, ami túlmutat a nyelvi tervezők legmerészebb álmain. Talán meglepő módon, tekintettel arra a tényre, hogy a sablonok funkcionális programok, a funkcionális programozók lassan kamatoztatták a C++ sikerét.
  4. ↑ Digital Mars : D Programozási nyelv 2.0  

Irodalom

  • David Vandevoerd, Nicholas M. Josattis. C ++ sablonok: A teljes útmutató = C++ sablonok: A teljes útmutató. - M . : " Williams " , 2003. - S.  544 . — ISBN 0-201-73484-2 .
  • Podbelsky V. V. 6.9. Függvénysablonok //6. fejezet Függvények, mutatók, hivatkozások // C++ nyelv / áttekintés. Dadaev Yu. G. - 4. - M . : Pénzügy és statisztika , 2003. - S. 230-236. — 560 p. - ISBN 5-279-02204-7 , UDC 004.438Si (075.8) LBC 32.973.26-018 1ya173.

Linkek