Generikus programozás

A Wikipédiából, a szabad enciklopédiából

A generikus programozás a számítógépes programozás egy olyan stílusa, amelyben az algoritmusok a később meghatározandó típusok formájában írják fel, amelyeket aztán szükség esetén megadnak a a paraméterként megadott típusokhoz. Ezt a megközelítést az ML programozási nyelv vezette be 1973[1]-ban[2], lehetővé teszi olyan közös függvények vagy típusok írását, amelyek csak abban a típusban különböznek egymástól, amelyen működnek, így csökkentve a párhuzamosságot. Az ilyen szoftverek entitások néven generikus Ada, C #, Delphi, Eiffel, F #, Java, Nim, Python, Rust, Swift, TypeScript és a Visual Basic .NET. Paraméteres polimorfizmusként ismerik őket az ML-ben, Scalában, Juliában és Haskellben (a Haskell-közösség a "generikus" kifejezést is használja egy kapcsolódó, de némileg eltérő fogalomra); sablonok C++ és D nyelven; és paraméterezett típusok a nagy hatású 1994-es Design Patterns[3] című könyvben.

A "generikus programozás" kifejezést eredetileg David Musser és Alexander Stepanov találta ki a fentieknél specifikusabban, egy olyan programozási paradigma leírására, amelyben a típusokra vonatkozó alapvető követelményeket elvonták az algoritmusok és adatstruktúrák konkrét példáitól, és formalizálták mint fogalmak, általános funkciókkal, amelyek e fogalmak szempontjából valósulnak meg, jellemzően a fent leírt nyelvi genericitási mechanizmusokat alkalmazva.

Stepanov–Musser és más generikus programozási paradigmák[szerkesztés]

Az általános programozást Musser & Stepanov (1989) a következőképpen határozza meg:   A "generikus programozás" paradigma a szoftverbontás megközelítése, amelynek során a típusokra vonatkozó alapvető követelményeket az algoritmusok és az adatstruktúrák konkrét példáiból elvonják és fogalommá formálják, analóg módon az algebrai elméletek absztrakt algebrai absztrakciójával. Ennek a programozási megközelítésnek a korai példáit a Scheme és az Ada hajtották végre, bár a legismertebb példa a Standard Template Library (STL), amely kifejlesztette az iterátorok elméletét, amelyet a szekvencia adatstruktúrák és az azokon működő algoritmusok leválasztására használnak.

Például adott N szekvencia adatstruktúra, pl. egyenként összekapcsolt lista, vektor stb. és M algoritmusok a működésükhöz megkeresés, rendezés stb., közvetlen megközelítéssel minden algoritmust konkrétan az egyes adatstruktúrákra valósítanának meg, N × M kombinációkat adva a megvalósításhoz. A generikus programozási megközelítésben azonban az egyes adatstruktúrák egy iterátor koncepció modelljét adják vissza (egyszerű értéktípus, amelyre le lehet vonni az aktuális érték lekérésére, vagy megváltoztatható, hogy a sorozat másik értékére mutasson), és minden algoritmust megírnak általában az ilyen iterátorok érveivel, pl egy iterátor pár, amely a feldolgozásra kerülő rész vagy tartomány kezdetére és végére mutat. Így csak N + M adatstruktúra-algoritmus kombinációkra van szükség. Az STL több iterátor fogalmat határoz meg, amelyek mindegyike egy szűkebb fogalom finomítását pl. az előremenő iterátorok csak a következő értékre biztosítanak mozgást egy szekvenciában (pl. alkalmasak külön-külön összekapcsolt listához vagy bemeneti adatfolyamhoz), míg a véletlen hozzáférésű iterátor közvetlen állandó idejű hozzáférést biztosít a szekvencia bármely eleméhez (például vektorhoz). Fontos szempont, hogy egy adatstruktúra a legáltalánosabb, hatékonyan megvalósítható koncepció modelljét adja vissza - a számítási bonyolultsági követelmények kifejezetten a fogalommeghatározás részét képezik. Ez korlátozza azokat az adatstruktúrákat, amelyekre egy adott algoritmus alkalmazható, és az ilyen bonyolultsági követelmények döntően meghatározzák az adatstruktúra választását. Az generikus programozást hasonlóan alkalmazták más területeken is, pl. gráfalgoritmusokban.

Megjegyezzük, hogy bár ez a megközelítés gyakran használja a fordítási idő genericitásának / sablonjainak nyelvi jellemzőit, valójában független bizonyos nyelv-technikai részletektől. Alekszandr Sztepanov, a programozás úttörője írta:

„Generic programming is about abstracting and classifying algorithms and data structures. It gets its inspiration from Knuth and not from type theory. Its goal is the incremental construction of systematic catalogs of useful, efficient and abstract algorithms and data structures. Such an undertaking is still a dream.”

– Alexander Stepanov

„I believe that iterator theories are as central to Computer Science as theories of rings or Banach spaces are central to Mathematics.”

– Alexander Stepanov

Bjarne Stroustrup megjegyezte,

Egyéb programozási paradigmák, amelyeket generikus programozásnak neveznek, magukban foglalják a Datatype generikus programozást, a "Generic Programming – an Introduction" részben leírtak szerint. A Scrap your boilerplate megközelítés egy könnyű, általános programozási megközelítés a Haskell számára.[7]

Ebben a cikkben megkülönböztetjük a fenti általános programozás magas szintű programozási paradigmáit az azok végrehajtásához használt alacsonyabb szintű programozási nyelv genericitási mechanizmusaitól(lásd: Programozási nyelv támogatása az általánossághoz ). Az általános programozási paradigmák további tárgyalásához és összehasonlításához lásd.[8]

Programozási nyelv támogatása általánosságban[szerkesztés]

Az általános szolgáltatások magas szintű nyelvekben legalább az 1970-es évek óta léteznek olyan nyelveken, mint az ML, CLU és Ada, és ezt követően számos objektumalapú és objektumorientált nyelv átvette őket, köztük a BETA, C++, D, Eiffel, Java, és a DEC mára megszűnt Trellis-Owl nyelve.

A genericitást a különböző programozási nyelvek különbözőképpen valósítják meg és támogatják; a "generikus" kifejezést másképp használták a különféle programozási összefüggésekben is. Például a Forthban a fordító lefordíthat kódot fordítás közben, és új fordítói kulcsszavakat és megvalósításokat hozhat létre ezekhez a szavakhoz menet közben. Kevés olyan szó van, amely kiteszi a fordító viselkedését, ezért természetesen olyan genericitást kínál, amelyre azonban a legtöbb Forth szöveg nem hivatkozik. Hasonlóképpen, a dinamikusan tipizált nyelvek, különösen az értelmezett nyelvek, alapértelmezés szerint általában genericitást kínálnak, mivel az értékek átadása a funkcióknak és az értékadás hozzárendelésük típus-közömbös, és ezt a viselkedést gyakran használják absztrakció vagy kód szűkszavúsága szempontjából, azonban ez általában nem címkézett általános, a nyelv által alkalmazott dinamikus gépelési rendszer közvetlen következménye. A kifejezést a funkcionális programozásban használták, különösen a Haskell-szerű nyelvekben, amelyek olyan szerkezeti típusú rendszert használnak, ahol a típusok mindig paraméteresek, és az adott típusok tényleges kódja általános. Ezek a felhasználások továbbra is a kódmentés és az absztrakció renderelésének hasonló célját szolgálják.

A tömbök és a struktúrák előre definiált általános típusokként tekinthetők. A tömb vagy a struktúra minden használata egy új konkrét típust eredményez, vagy újból felhasznál egy korábbi példányt. A tömb és a struktúra elemtípusok paraméterezett típusok, amelyek a megfelelő általános típus példázására szolgálnak. Mindez általában beépül a fordítóba, és a szintaxis eltér a többi általános konstrukciótól. Néhány kibővíthető programozási nyelv megpróbálja egységesíteni a beépített és a felhasználó által meghatározott általános típusokat.

A programozási nyelvek genericitási mechanizmusainak átfogó felmérése következik. Egy specifikus felméréshez, amely összehasonlítja a mechanizmusok alkalmasságát az általános programozásra.

Objektumorientált nyelvekben[szerkesztés]

Ha a konténerosztályokat statikusan tipizált nyelveken hozza létre, kényelmetlen konkrét megvalósításokat írni az egyes tartalmazott adattípusokhoz, különösen, ha az egyes adattípusok kódja gyakorlatilag megegyezik. Például a C++ nyelven a kód duplikációja megkerülhető egy osztálysablon meghatározásával:

template<typename T>
class List {
  // Class contents.
};

List<Animal> list_of_animals;
List<Car> list_of_cars;

Fent a T helyőrzője annak a típusnak, amelyet a lista létrehozásakor megadnak. Ezek a "T-típusú konténerek", amelyeket sablonoknak hívnak, lehetővé teszik egy osztály újra felhasználását különböző adattípusokkal, mindaddig, amíg bizonyos szerződéseket, például altípusokat és aláírást megtartanak. Ezt az általános jellegű mechanizmust nem szabad összetéveszteni az inklúziós polimorfizmussal, amely a cserélhető alosztályok algoritmikus használata: például egy Moving_Object típusú objektumok listája, amelyek Animal és Car típusú objektumokat tartalmaznak. A sablonok típusfüggetlen funkciókhoz is használhatók, mint az alábbi Swap példában:

// "&" denotes a reference
template<typename T>
void Swap(T& a, T& b) { // A similar, but safer and potentially faster function 
                        // is defined in the standard library header <utility>
  T temp = b;
  b = a;
  a = temp;
}

std::string world = "World!";
std::string hello = "Hello, ";
Swap(world, hello);
std::cout << world << hello << ‘\n;  // Output is "Hello, World!".

A fent használt C ++ sablonkonstrukciót széles körben idézik , mint azon generikus konstrukciót, amely népszerűsítette a fogalmat a programozók és nyelvtervezők körében, és számos általános programozási idiómát támogat. A D programozási nyelv a C ++ precedensen alapuló, de egyszerűsített szintaxissal teljesen generikus képes sablonokat is kínál. A Java programozási nyelv a J2SE 5.0 bevezetése óta szintaktikailag a C++-on alapuló generikus lehetőségeket kínál.

A C# 2.0, az Oxygene 1.5 (más néven Chrome) és a Visual Basic .NET 2005 olyan konstrukciókkal rendelkezik, amelyek kihasználják a Microsoft .NET-keretrendszerben a 2.0-ás verzió óta meglévő generikumok támogatását.

Generikus Adában[szerkesztés]

Az Adának már azóta is vannak generikus termékei, amióta 1977–1980-ig tervezték. A szabványos könyvtár általános szolgáltatásokat használ számos szolgáltatás nyújtásához. Az Ada 2005 egy átfogó, általános tároló könyvtárat egészít ki a standard könyvtárban, amelyet a C++ szabványos sablonkönyvtár ihletett.

Az általános egység olyan csomag vagy alprogram, amely egy vagy több általános formális paramétert vesz fel.

Egy általános formális paraméter egy érték, egy változó, egy konstans, egy típus, egy alprogram, vagy akár egy másik kijelölt generikus egység példánya. Általános formális típusok esetében a szintaxis különbséget tesz diszkrét, lebegőpontos, fixpontos, hozzáférési (mutató) típusok stb. Között. Egyes formális paraméterek alapértelmezett értékekkel rendelkezhetnek.

Egy általános egység példányosításához a programozó átadja az egyes formálisok tényleges paramétereit. Az általános példány ekkor ugyanúgy viselkedik, mint bármely más egység. Lehetséges generikus egységek futás közbeni példányosítása, például egy hurok belsejében.

Példa[szerkesztés]

Az általános csomag specifikációja:

 generic
    Max_Size : Natural; -- a generic formal value
    type Element_Type is private; -- a generic formal type; accepts any nonlimited type
 package Stacks is
    type Size_Type is range 0 .. Max_Size;
    type Stack is limited private;
    procedure Create (S : out Stack;
                      Initial_Size : in Size_Type := Max_Size);
    procedure Push (Into : in out Stack; Element : in Element_Type);
    procedure Pop (From : in out Stack; Element : out Element_Type);
    Overflow : exception;
    Underflow : exception;
 private
    subtype Index_Type is Size_Type range 1 .. Max_Size;
    type Vector is array (Index_Type range <>) of Element_Type;
    type Stack (Allocated_Size : Size_Type := 0) is record
       Top : Index_Type;
       Storage : Vector (1 .. Allocated_Size);
    end record;
 end Stacks;

A generic csomag azonnali végrehajtása:

 type Bookmark_Type is new Natural;
 -- records a location in the text document we are editing

 package Bookmark_Stacks is new Stacks (Max_Size => 20,
                                        Element_Type => Bookmark_Type);
 -- Allows the user to jump between recorded locations in a document

Generic csomag példányának használata:

 type Document_Type is record
    Contents : Ada.Strings.Unbounded.Unbounded_String;
    Bookmarks : Bookmark_Stacks.Stack;
 end record;

 procedure Edit (Document_Name : in String) is
   Document : Document_Type;
 begin
   -- Initialise the stack of bookmarks:
   Bookmark_Stacks.Create (S => Document.Bookmarks, Initial_Size => 10);
   -- Now, open the file Document_Name and read it in...
 end Edit;
Előnyök és korlátozások[szerkesztés]

A nyelvi szintaxis lehetővé teszi az általános formális paraméterekre vonatkozó korlátozások pontos meghatározását. Például meghatározható, hogy egy általános formális típus csak egy moduláris típust fogad el ténylegesnek. Lehetséges az általános formai paraméterek közötti korlátozások kifejezése is; például:

 generic
    type Index_Type is (<>); -- must be a discrete type
    type Element_Type is private; -- can be any nonlimited type
    type Array_Type is array (Index_Type range <>) of Element_Type;

Ebben a példában az Array_Type-t mind az Index_Type, mind az Element_Type korlátozza. Az egység példányosításakor a programozónak át kell adnia egy tényleges tömbtípust, amely kielégíti ezeket a korlátozásokat.

Ennek a finomszemcsés vezérlésnek a hátránya egy bonyolult szintaxis, de mivel az összes általános formális paraméter teljesen meghatározva van a specifikációban, a fordító képes példányokat generálni anélkül, hogy megnézné az általános törzsét.

A C++-tól eltérően az Ada nem engedélyezi a speciális generikus példányokat, és megköveteli, hogy minden generikust kifejezetten példányosítsanak. Ezeknek a szabályoknak számos következménye van:

  • a fordító megosztott generikusokat valósíthat meg: egy általános egység objektumkódja megosztható minden példány között (kivéve, ha a programozó természetesen alprogramok beillesztését kéri). További következményekként:
    • nincs lehetőség a kód felpuffadására (a kód felpuffadás gyakori a C ++ nyelven, és különös gondosságot igényel, amint az alábbiakban kifejtésre kerül).
    • lehetséges generikusok példányosítása futás közben, valamint fordítási időben, mivel új példányhoz nincs szükség új objektumkódra.
    • az általános formális tárgynak megfelelő tényleges objektumokat nem mindig statikusnak tekintjük az általános formában; a részletekért és következményekért lásd a Wikikönyv általános formális tárgyait.
  • ha egy generikus változat teljesen azonos, könnyebb áttekinteni és megérteni a mások által írt programokat; nincsenek "különleges esetek", amelyeket figyelembe kellene venni.
  • mivel minden példány egyértelmű, nincsenek rejtett példányok, amelyek megnehezíthetik a program megértését.
  • Ada nem engedélyezi a "sablon metaprogramozását", mert nem engedélyezi a specializációkat.

Sablonok C++ nyelven[szerkesztés]

A C ++ sablonokkal engedélyezi az általános programozási technikákat. A C++ Standard Library tartalmazza a Standard Template Libraryt (STL), amely sablonok kereteit biztosítja a közös adatstruktúrákhoz és algoritmusokhoz. A C ++ formátumú sablonok szintén használhatók a sablon metaprogramozásához, amely a kód egy részének előre történő értékelése a fordítás idején, nem pedig a futás idején. A sablonspecializáció segítségével a C ++ sablonokat befejezettnek tekintjük.

Technikai áttekintés[szerkesztés]

Sokféle sablon létezik: függvénysablonok, osztálysablonok ... A függvénysablon a szokásos függvények létrehozásának mintája a példányosításkor megadott paraméterezési típusok alapján. Például a C++ Standard Template Library tartalmazza a max (x, y) függvénysablont, amely olyan funkciókat hoz létre, amelyek x vagy y értéket adnak vissza, attól függően, hogy melyik nagyobb. A max() így definiálható:

template<typename T>
T max(T x, T y) {
  return x < y ? y : x;
}

Ennek a függvénysablonnak a specializációit, a konkrét típusokkal való példányosításokat ugyanúgy lehet hívni, mint egy közönséges függvényt:

std::cout << max(3, 7);  // Outputs 7.

A fordító megvizsgálja a max hívásához használt argumentumokat, és megállapítja, hogy ez a max (int, int) hívása. Ezután példányosítja a függvény egy változatát, ahol a T típusú paraméterezés int, ezzel egyenértékűvé téve a következő függvényt:

int max(int x, int y) {
  return x < y ? y : x;
}

Ez akkor működik, hogy az x és y argumentumok egész számok, karakterláncok vagy bármilyen más típusok, amelyekre az x <y kifejezés értelmes, vagy pontosabban bármely olyan típusra, amelyre az < operátor definiálva van. A használható típuskészlethez nincs szükség közös öröklődésre, ezért nagyon hasonlít a kacsa típusozásra. Egy egyedi adattípust meghatározó program az operátor túltöltésével meghatározhatja a < jelentését az adott típushoz, ezáltal lehetővé téve annak használatát a max () függvénysablonnal. Bár ez az elkülönített példában csekély előnynek tűnhet, egy olyan átfogó könyvtár összefüggésében, mint az STL, lehetővé teszi a programozó számára, hogy széles körű funkcionalitást szerezzen egy új adattípushoz, csupán néhány operátor meghatározásával. A < pusztán definiálása lehetővé teszi egy típus használatát a sort(), stable_sort(), and binary_search() algoritmusokkal, vagy olyan adatstruktúrákba, mint halmazok, kupacok és asszociatív tömbökbe helyezhető.

A C++ sablonok fordításkor teljesen típusbiztosak. Bemutatásként a standard típusú komplex nem definiálja az < operátort, mert a komplex számokon nincs szigorú sorrend. Ezért max (x, y) fordítási hibával meghiúsul, ha x és y komplex értékek. Hasonlóképpen, a (z) < -ra támaszkodó más sablonok nem alkalmazhatók összetett adatokra, hacsak nem biztosítunk összehasonlítást (funkció vagy funkció formájában). Például: Egy komplexum nem használható kulcsként egy térképhez, hacsak nincs összehasonlítás. Sajnos a fordítók történelmileg kissé ezoterikus, hosszú és haszontalan hibaüzeneteket generálnak az ilyen típusú hibákhoz. Annak biztosítása, hogy egy bizonyos objektum betartsa a metódusprotokollt, enyhítheti ezt a problémát. Azok a nyelvek, amelyek <helyett az összehasonlítást használják, összetett értékeket is használhatnak kulcsként.

Egy másik típusú sablon, egy osztálysablon, ugyanazt a koncepciót kiterjeszti az osztályokra is. Az osztálysablon specializáció egy osztály. Az osztálysablonokat gyakran használják általános tárolók készítéséhez. Például az STL-nek van egy összekapcsolt listatárolója. Az egész számok összekapcsolt listájának elkészítéséhez írja az list <int>-et. A karaktersorozatok listája a list <string>. A listához tartozik egy sor szabványos funkció, amelyek bármilyen kompatibilis paraméterezési típushoz használhatók.

Sablon specializáció[szerkesztés]

A C ++ sablonjainak egyik fontos jellemzője a sablonok specializációja. Ez lehetővé teszi alternatív megvalósítások biztosítását a pillanatnyilag paraméterezett típus bizonyos jellemzői alapján. A sablon-specializációnak két célja van: az optimalizálás bizonyos formáinak lehetővé tétele és a kódduzzadás csökkentése.

Vegyünk például egy sort () sablonfüggvényt. Az egyik elsődleges tevékenység, amelyet egy ilyen funkció végez, az értékek cseréje vagy cseréje a konténer két pozíciójában. Ha az értékek nagyok (az egyes tárolásához szükséges bájtok számát tekintve), akkor gyakran gyorsabb, ha először külön mutató listát készítünk az objektumokra, rendezzük ezeket a mutatókat, majd elkészítjük a végső rendezett sorrendet. Ha az értékek meglehetősen kicsik, akkor általában a leggyorsabb az értékeket a helyükön cserélni, ha szükséges. Továbbá, ha a paraméterezett típus már valamilyen mutató típusú, akkor nincs szükség külön mutató tömb építésére. A sablon-specializáció lehetővé teszi a sablonkészítő számára, hogy különböző megvalósításokat írjon, és meghatározza azokat a jellemzőket, amelyekkel a paraméterezett típus/(ok)-nak rendelkezniük kell az egyes megvalósításokhoz.

A függvénysablonokkal ellentétben az osztálysablonok részben specializálódhatnak. Ez azt jelenti, hogy az osztály sablonkódjának alternatív változata akkor nyújtható, ha a sablon néhány paramétere ismert, míg a többi sablon paraméter általános marad. Ezt fel lehet használni például egy alapértelmezett megvalósítás (az elsődleges specializáció) létrehozására, amely feltételezi, hogy egy paraméterező típus másolása drága, majd részleges specializációkat hozhat létre olyan típusok számára, amelyek olcsón másolhatók, ezáltal növelve az általános hatékonyságot. Az ilyen osztálysablon kliensei csak annak szakterületeit használják, anélkül, hogy tudnunk kellene, hogy a fordító minden esetben használta-e az elsődleges vagy részleges specializációt. Az osztálysablonok teljes mértékben specializálódhatnak is, ami azt jelenti, hogy alternatív megvalósítás biztosítható, ha az összes paraméterező típus ismert.

Előnyök és hátrányok[szerkesztés]

A sablonok egyes felhasználásait, például a max () függvényt, korábban függvényszerű előprocesszor makrók töltötték ki (a C programozási nyelv öröksége). Például itt van egy ilyen makró lehetséges megvalósítása:

#define max(a,b) ((a) < (b) ? (b) : (a))

A makrókat az előprocesszor kibővíti (a másolatot beilleszti) a megfelelő fordítás előtt; a sablonok tényleges valós funkciók. A makrók mindig kibővítettek sorban; a sablonok akkor is helyben kifejtett függvények lehetnek, ha a fordító megfelelőnek tartja.

A sablonokat azonban általában a makrókkal szembeni javulásnak tekintik e célból. A sablonok típusbiztonságosak. A sablonok elkerülik a kódban található olyan gyakori hibákat, amelyek nagyban kihasználják a funkciószerű makrókat, például kétszer értékelik a mellékhatásokkal járó paramétereket. Talán a legfontosabb, hogy a sablonokat úgy tervezték, hogy sokkal nagyobb problémákra alkalmazhatók legyenek, mint a makrók.

A sablonok használatának négy elsődleges hátránya van: támogatott szolgáltatások, a fordító támogatása, gyenge hibaüzenetek (általában pre C++ 20 SFINAE esetén) és a kódfelfúvódása:

  1. A C++ - ban található sablonokból sok szolgáltatás hiányzik, ami gyakran lehetetlenné teszi azok megvalósítását és egyszerű használatát. Ehelyett a programozóknak bonyolult trükkökre kell támaszkodniuk, amelyek duzzadt, nehezen érthető és nehezen karbantartható kódokhoz vezetnek. A C++ szabványok jelenlegi fejleményei súlyosbítják ezt a problémát azáltal, hogy erőteljesen kihasználják ezeket a trükköket, és rengeteg új funkciót építenek rájuk vagy szem előtt tartva a sablonokat.
  2. Számos fordító történelmileg gyengén támogatta a sablonokat, így a sablonok használata kissé kevésbé hordozhatóvá tehette volna a kódot. A támogatás akkor is gyenge lehet, ha egy C ++ fordítót olyan összekapcsolóval használnak, amely nem ismeri a C++ - t, vagy amikor sablonokat próbál meg megosztott könyvtár határain túl használni.
  3. A fordítók zavaros, hosszú és néha haszontalan hibaüzeneteket készíthetnek, amikor hibákat észlelnek az SFINAE-t használó kódban. Ez megnehezítheti a sablonok fejlesztését.
  4. Végül a sablonok használatához a fordítónak meg kell adnia a sablon osztály vagy függvény külön példányát a vele használt típusú paraméterek minden permutációjához. Erre azért van szükség, mert a C ++ típusú típusok nem egyformák, és az adatmezők nagysága fontos az osztályok működéséhez.) Tehát a sablonok válogatás nélküli használata kódfelfúvódáshoz vezethet, ami túl nagy futtatható fájlokat eredményezhet. A sablonok specializálásának és levezetésének megfontolt használata azonban egyes esetekben drámaian csökkentheti az ilyen kódot:

A sablonok által generált extra példányok bizonyos hibakeresőket is nehézségekbe ütközhetnek a sablonokkal való kecsesen. Például egy hibakeresési töréspont beállítása egy sablonon egy forrásfájlból elmulaszthatja a töréspont beállítását a kívánt aktuális példányosításban, vagy beállíthat egy töréspontot a sablon minden egyes példányában.

Ezenkívül a sablon megvalósításának forráskódjának teljes mértékben elérhetőnek kell lennie (pl. Egy fejlécben) az azt használó fordítóegység (forrásfájl) számára. Ha a fejlécfájlokban nem szerepelnek, a sablonok, beleértve a Standard könyvtár nagy részét, nem állíthatók össze. (Ez ellentétben áll a nem sablonos kódokkal, amelyeket binárisan lehet fordítani, és csak egy deklarációk fejlécfájlt adunk meg a kódot használva.) Ez hátrányt jelenthet a megvalósító kód feltárásával, amely eltávolít néhány kivonatot, és korlátozhatja annak felhasználás zárt forráskódú projektekben.

Sablonok D-ben[szerkesztés]

A D (programozási nyelv) támogatja a C++-on alapuló sablonokat. A legtöbb C++ sablon-idióma változások nélkül átkerül a D-be, de D további funkciókat ad hozzá:

  • A D sablonparaméterei nem csak a típusokra és a primitív értékekre korlátozódnak (mint a C++-ban a C++20 előtt), hanem tetszőleges fordítási időértékeket (például karakterláncokat és strukturális literálokat) és álneveket is tetszőleges azonosítókhoz engedélyeznek, beleértve egyéb sablonok vagy sablonpéldányok.
  • A sablonmegkötések és a static if utasítás alternatívát kínálnak a C++ fogalmaival és if constexpr.
  • Az is (...) kifejezés lehetővé teszi a spekulatív példányosítással az objektum tulajdonságainak ellenőrzését a fordítás idején.
  • Az automatikus kulcsszó és a kivétel typeof lehetővé teszi a következtetést a változó deklarációkra és a függvény visszatérési értékeire, ami viszont engedélyezi a "Voldemort típusokat" (olyan típusokat, amelyek nem rendelkeznek globális névvel).

A D-ben lévő sablonok más szintaxist használnak, mint a C++-nál: míg a C++-ban a sablonok szögletes zárójelbe vannak csomagolva (Template<param1, param2>), addig a D felkiáltójelet és zárójelet használ: Template! (Param1, param2). Ez elkerüli a C++ elemzési nehézségeket, mivel az összehasonlító operátorokkal kapcsolatos kétértelműség adódik. Ha csak egy paraméter van, akkor a zárójeleket el lehet hagyni.

Hagyományosan a D egyesíti a fenti jellemzőket, hogy tulajdonság-alapú általános programozással fordítási idejű polimorfizmust biztosítson. Például egy bemeneti tartomány minden olyan típusként definiálható, amely kielégíti az isInputRange által elvégzett ellenőrzéseket, amelyet a következőképpen definiálnak:

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    (inout int = 0)
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

Csak egy bemeneti tartományt elfogadó függvény használhatja a fenti sablont egy sablon-kényszerben:

auto fun(Range)(Range range)
    if (isInputRange!Range)
{
    // ...
}
Kódgenerálás[szerkesztés]

A sablon metaprogramozása mellett a D számos funkciót is kínál a fordítási idő kódgenerálásának engedélyezéséhez:

  • Az import kifejezés lehetővé teszi a fájl lemezről történő olvasását és tartalmának karakterlánc-kifejezésként történő használatát.
  • A fordítási idő tükrözése lehetővé teszi a deklarációk és tagjaik felsorolását és ellenőrzését az összeállítás során.
  • A felhasználó által definiált attribútumok lehetővé teszik a felhasználók számára, hogy tetszőleges azonosítókat csatoljanak a deklarációkhoz, amelyeket aztán össze lehet állítani a fordítási idő tükrözésével.
  • A Compile-Time Function Execution (CTFE) lehetővé teszi a D (biztonságos műveletekre korlátozott) részhalmazának értelmezését a fordítás során.
  • A karakterlánc-mixek lehetővé teszik egy string-kifejezés tartalmának kiértékelését és összeállítását D-kódként, amely a program részévé válik.

A fentiek kombinálása lehetővé teszi a kód generálását a meglévő deklarációk alapján. Például a D sorosítási keretek felsorolhatják a típus tagjait, és minden sorosított típushoz speciális funkciókat generálhatnak a sorosítás és a deserializáció elvégzéséhez. A felhasználó által definiált attribútumok tovább jelezhetik a sorosítási szabályokat.

Az import kifejezés és a fordítási idő függvény végrehajtása szintén lehetővé teszi a tartományspecifikus nyelvek hatékony megvalósítását. Például egy olyan függvénynél, amely egy HTML sablont tartalmazó karaktersorozatot vesz fel, és egyenértékű D forráskódot ad vissza, akkor a következő módon lehet használni:

// Import the contents of example.htt as a string manifest constant.
enum htmlTemplate = import("example.htt");

// Transpile the HTML template to D code.
enum htmlDCode = htmlTemplateToD(htmlTemplate);

// Paste the contents of htmlDCode as D code.
mixin(htmlDCode);

Általánosságok Eiffelben[szerkesztés]

Az általános osztályok az eredeti metódus és nyelvtervezés óta az Eiffel részét képezik. Az Eiffel alapítványi publikációi a genericitás kifejezést használják az általános osztályok létrehozásának és használatának leírására.

Alapvető / korlátlan általánosságok[szerkesztés]

Az általános osztályokat osztálynevükkel és egy vagy több hivatalos általános paraméter listájával deklaráljuk. A következő kódban a LIST osztálynak van egy formális általános G paramétere

class
    LIST [G]
            ...
feature   -- Access
    item: G
            -- The item currently pointed to by cursor
            ...
feature   -- Element change
    put (new_item: G)
            -- Add `new_item' at the end of the list
            ...

A formális általános paraméterek tetszőleges osztálynevek helyőrzői, amelyeket a generikus osztály deklarálásakor kell megadni, amint az az alábbi két általános származtatásban látható, ahol az ACCOUNT és DEPOSIT más osztálynév. Az ACCOUNT és a DEPOSIT tényleges általános paramétereknek számítanak, mivel valós osztályneveket adnak a G helyettesítésére a tényleges használat során.

    list_of_accounts: LIST [ACCOUNT]
            -- Account list

    list_of_deposits: LIST [DEPOSIT]
            -- Deposit list

Az Eiffel típusú rendszeren belül, bár a LIST [G] osztály osztálynak számít, nem tekinthető típusnak. A LIST [G] általános levezetése, például a LIST [ACCOUNT] azonban típusnak tekinthető.

Korlátozott általánosságok[szerkesztés]

A fenti listás osztály esetében a G -t helyettesítő tényleges általános paraméter bármely más rendelkezésre álló osztály lehet. Annak az osztályhalmaznak a korlátozására, amelyből érvényes tényleges általános paraméterek választhatók, meg lehet adni egy általános korlátozást. Az alábbi SORTED_LIST osztály deklarációban az általános megkötés előírja, hogy bármely érvényes tényleges általános paraméter olyan osztály lesz, amely a

COMPARABLE osztályból öröklődik. Az általános megkötés biztosítja, hogy a SORTED_LIST elemei valóban rendezhetők legyenek.

class
    SORTED_LIST [G -> COMPARABLE]

Generics in Java[szerkesztés]

2004-ben a J2SE 5.0 részeként hozzáadták a Java programozási nyelvhez az általános vagy a "T-type-type-type" támogatást. A Java-ban a generikusokat csak fordításkor ellenőrizzük a típushelyesség szempontjából. A generikus típusú információkat ezután a törlés nevű folyamaton keresztül eltávolítják, hogy fenntartsák a kompatibilitást a régi JVM-implementációkkal, ami futás közben nem érhető el. Például a List <String> konvertálódik a nyers List listává. A fordító beszúrja a típuskényszerítést, hogy az elemeket String típusúvá alakítsa, amikor lekerülnek a listáról, csökkentve a teljesítményt más megvalósításokhoz, például a C ++ sablonokhoz képest.

Általánosságok a .NET [C#, VB.NET]-ben[szerkesztés]

A generikus gyógyszereket a .NET Framework 2.0 részeként adták hozzá 2005 novemberében, a Microsoft Research 1999-ben elindított kutatási prototípusán alapulva. Noha a Java-generikusokhoz hasonlóan, a .NET-generikusok sem alkalmazzák a típusok törlését, hanem az általánosítást első osztályú mechanizmusként valósítják meg a futás során a reifikálás segítségével. Ez a tervezési lehetőség további funkcionalitást kínál, például lehetővé teszi a reflexiót az általános típusok megőrzésével, valamint enyhíti a törlés néhány korlátozását (például nem képes generikus tömbök létrehozására). Ez azt is jelenti, hogy a futásidők és a normálisan drága doboz-átalakítások nem érnek el teljesítményt. Ha primitív és értéktípusokat használnak általános érvekként, akkor speciális megvalósításokat kapnak, lehetővé téve a hatékony általános gyűjteményeket és metóduseket. Csakúgy, mint a C ++ és a Java esetében, a beágyazott általános típusok, mint például a Szótár <string, Lista <int>>, érvényesek, azonban a kódanalízis tervezési szabályaiban a tagok aláírása esetén nem ajánlott.

A .NET a generikus típusú korlátozások hat változatát teszi lehetővé, ahol a kulcsszó, ideértve az általános típusok korlátozását is, hogy értéktípusok, osztályok legyenek, konstruktorokkal rendelkezzenek és interfészeket valósítsanak meg. Az alábbiakban egy példa egy interfész-korlátozással:

using System;

class Sample
{
    static void Main()
    {
        int[] array = { 0, 1, 2, 3 };
        MakeAtLeast<int>(array, 2); // Change array to { 2, 2, 2, 3 }
        foreach (int i in array)
            Console.WriteLine(i); // Print results.
        Console.ReadKey(true);
    }

    static void MakeAtLeast<T>(T[] list, T lowest) where T : IComparable<T>
    {
        for (int i = 0; i < list.Length; i++)
            if (list[i].CompareTo(lowest) < 0)
                list[i] = lowest;
    }
}

MakeAtLeast () metódus lehetővé teszi a tömbökön történő műveletet, általános T. típusú elemekkel. A metódus típuskorlátozása azt jelzi, hogy a metódus bármely olyan T típusra alkalmazható, amely megvalósítja az általános IComparable <T> interfészt. Ez biztosítja a fordítási idő hibáját, ha a metódust meghívják, ha a típus nem támogatja az összehasonlítást. Az interfész biztosítja a CompTo (T) általános metódust.

A fenti metódus írható általános típusok nélkül is, egyszerűen a nem általános Array típust használva. Mivel azonban a tömbök ellentmondásosak, az öntés nem lenne biztonságos a típus szempontjából, és a fordító nem találna bizonyos lehetséges hibákat, amelyeket egyébként elkapnának az általános típusok használata során. Ezenkívül a metódusnak objektumokként kellene elérnie a tömb elemeit, és két elem összehasonlításához castingra lenne szükség. (Az értéktípusokhoz, például az int típusokhoz, ez doboz-átalakítást igényel, bár ezt meg lehet

oldani a Comparer <T> osztály használatával, ahogy az a szokásos gyűjtési osztályokban történik.)

A statikus tagok figyelemre méltó viselkedése egy általános .NET osztályban a statikus tagok példányosítása futási időnként (lásd az alábbi példát).

    //A generic class
    public class GenTest<T>
    {
        //A static variable - will be created for each type on reflection
        static CountedInstances OnePerType = new CountedInstances();

        //a data member
        private T mT;

        //simple constructor
        public GenTest(T pT)
        {
            mT = pT;
        }
    }

    //a class
    public class CountedInstances
    {
        //Static variable - this will be incremented once per instance
        public static int Counter;

        //simple constructor
        public CountedInstances()
        {
            //increase counter by one during object instantiation
            CountedInstances.Counter++;
        }
    }

  //main code entry point
  //at the end of execution, CountedInstances.Counter = 2
  GenTest<int> g1 = new GenTest<int>(1);
  GenTest<int> g11 = new GenTest<int>(11);
  GenTest<int> g111 = new GenTest<int>(111);
  GenTest<double> g2 = new GenTest<double>(1.0);

Általánosságok Delphiben[szerkesztés]

A Delphi Object Pascal dialektus a Delphi 2007 kiadásban szerzett generikus anyagokat, kezdetben csak a (most már megszűnt).NET fordítóval, mielőtt hozzáadták volna a natív kódhoz a Delphi 2009 kiadásban. A Delphi generikusok szemantikája és képességei nagyrészt azokra a mintákra épülnek, amelyeket a .NET 2.0-ban a generikusok rendelkeztek, bár a megvalósítás szükségszerűen egészen más. Itt van egy nagyjából közvetlen fordítás a fenti C # első példáról:

program Sample;

{$APPTYPE CONSOLE}

uses
  Generics.Defaults; //for IComparer<>

type
  TUtils = class
    class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T;
      Comparer: IComparer<T>); overload;
    class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T); overload;
  end;

class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T;
  Comparer: IComparer<T>);
var
  I: Integer;
begin
  if Comparer = nil then Comparer := TComparer<T>.Default;
  for I := Low(Arr) to High(Arr) do
    if Comparer.Compare(Arr[I], Lowest) < 0 then
      Arr[I] := Lowest;
end;

class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T);
begin
  MakeAtLeast<T>(Arr, Lowest, nil);
end;

var
  Ints: TArray<Integer>;
  Value: Integer;
begin
  Ints := TArray<Integer>.Create(0, 1, 2, 3);
  TUtils.MakeAtLeast<Integer>(Ints, 2);
  for Value in Ints do
    WriteLn(Value);
  ReadLn;
end.

A C # -hoz hasonlóan a metódusoknak és az egész típusoknak is lehet egy vagy több típusparaméterük. A példában a TArray egy általános típus (amelyet a nyelv határoz meg), a MakeAtLeast pedig egy általános metódus. Az elérhető korlátozások nagyon hasonlítanak a C # -ben elérhető korlátozásokhoz: bármilyen értéktípus, bármely osztály, egy adott osztály vagy interfész és egy paraméter nélküli konstruktorral rendelkező osztály. A többféle kényszer additív unióként működik.

Általánosságok Free Pascalban[szerkesztés]

A Free Pascal generikus alkalmazásokat hajtott végre a Delphi előtt, különböző szintaxissal és szemantikával. Az FPC 2.6.0 verziója óta azonban a Delphi stílusú szintaxis elérhető a {$ mode Delphi} nyelvi mód használatakor. Így a Free Pascal programozói képesek generikusokat használni, bármelyik stílusban, amelyet jobban szeretnek.

Delphi és Free Pascal példa:

// Delphi style
unit A;

{$ifdef fpc}
  {$mode delphi}
{$endif}

interface

type
  TGenericClass<T> = class
    function Foo(const AValue: T): T;
  end;

implementation

function TGenericClass<T>.Foo(const AValue: T): T;
begin
  Result := AValue + AValue;
end;

end.

// Free Pascal's ObjFPC style
unit B;

{$ifdef fpc}
  {$mode objfpc}
{$endif}

interface

type
  generic TGenericClass<T> = class
    function Foo(const AValue: T): T;
  end;

implementation

function TGenericClass.Foo(const AValue: T): T;
begin
  Result := AValue + AValue;
end;

end.

// example usage, Delphi style
program TestGenDelphi;

{$ifdef fpc}
  {$mode delphi}
{$endif}

uses
  A,B;

var
  GC1: A.TGenericClass<Integer>;
  GC2: B.TGenericClass<String>;
begin
  GC1 := A.TGenericClass<Integer>.Create;
  GC2 := B.TGenericClass<String>.Create;
  WriteLn(GC1.Foo(100)); // 200
  WriteLn(GC2.Foo('hello')); // hellohello
  GC1.Free;
  GC2.Free;
end.

// example usage, ObjFPC style
program TestGenDelphi;

{$ifdef fpc}
  {$mode objfpc}
{$endif}

uses
  A,B;

// required in ObjFPC
type
  TAGenericClassInt = specialize A.TGenericClass<Integer>;
  TBGenericClassString = specialize B.TGenericClass<String>;
var
  GC1: TAGenericClassInt;
  GC2: TBGenericClassString;
begin
  GC1 := TAGenericClassInt.Create;
  GC2 := TBGenericClassString.Create;
  WriteLn(GC1.Foo(100)); // 200
  WriteLn(GC2.Foo('hello')); // hellohello
  GC1.Free;
  GC2.Free;
end.

Funkcionális nyelvek[szerkesztés]

Általánosságok Haskell-ben[szerkesztés]

A Haskell típusosztály-mechanizmusa támogatja az általános programozást. A Haskell előre definiált típusosztályai közül hatnak (beleértve az Eq-t, az egyenlőség szempontjából összehasonlítható típusokat, és a Show-t, amelyeknek az értékei karakterláncként ábrázolhatók) különleges tulajdonsága a származtatott példányok támogatása. Ez azt jelenti, hogy egy új típust definiáló programozó kijelentheti, hogy ennek a típusnak ezen speciális típusú osztályok egyikének kell lennie, anélkül, hogy az osztály metódusainak megvalósítását biztosítaná, amire az osztálypéldányok deklarálásakor általában szükség van. Az összes szükséges metódust "kivezetik" - vagyis automatikusan elkészítik - a típus szerkezete alapján. Például egy bináris fafajta következő deklarációja kimondja, hogy az Eq és Show osztályok példányának kell lennie:

data BinTree a = Leaf a | Node (BinTree a) a (BinTree a)
      deriving (Eq, Show)

Ez azt eredményezi, hogy az egyenlőségfüggvény (==) és a karakterlánc-reprezentációs függvény (show) automatikusan definiálásra kerül a BinTree T űrlap bármely típusához, feltéve, hogy T maga is támogatja ezeket a műveleteket.

Az Eq és a Show származtatott példányainak támogatása metóduseik == -ig teszik lehetővé, és a para-metrikusan polimorf függvényektől minőségileg eltérő módon mutatják az általános képeket: ezek a "függvények" (pontosabban típus-indexelt függvénycsaládok) alkalmazhatók a különféle típusok, és bár minden argumentumtípusonként eltérő módon viselkednek, kevés munka szükséges egy új típus támogatásához. Ralf Hinze (2004) kimutatta, hogy a felhasználó által definiált típusú osztályokhoz hasonló hatás érhető el bizonyos programozási technikákkal. Más kutatók ennek és másfajta nagylelkűségnek a megközelítését javasolták a Haskell és a Haskell kiterjesztéseivel összefüggésben (az alábbiakban tárgyaljuk).

PolyP[szerkesztés]

A PolyP volt az első általános programozási nyelv kiterjesztés a Haskellhez. A PolyP-ben az általános függvényeket politipikusnak nevezzük. A nyelv egy speciális konstrukciót vezet be, amelyben az ilyen politípusos funkciók strukturális indukcióval definiálhatók egy szabályos adattípus mintafunkciójának struktúráján. A PolyP rendszeres adattípusai a Haskell adattípusok részhalmaza. A t reguláris adattípusnak * → * típusúnak kell lennie, és ha a definícióban az a formális típusú argumentum, akkor minden t-re irányuló rekurzív hívásnak t a formájúnak kell lennie. Ezek a korlátozások kizárják a magasabb típusú adattípusokat, valamint a beágyazott adattípusokat, ahol a rekurzív hívások más formájúak. A PolyP lapítás funkciója itt példaként szolgál:

   flatten :: Regular d => d a -> [a]
   flatten = cata fl

   polytypic fl :: f a [a] -> [a]
     case f of
       g+h -> either fl fl
       g*h -> \(x,y) -> fl x ++ fl y
       () -> \x -> []
       Par -> \x -> [x]
       Rec -> \x -> x
       d@g -> concat . flatten . pmap fl
       Con t -> \x -> []

   cata :: Regular d => (FunctorOf d a b -> b) -> d a -> b
Generic Haskell[szerkesztés]

A Generic Haskell a Haskell újabb kiterjesztése, amelyet a hollandiai Utrechti Egyetemen fejlesztettek ki. Az általa nyújtott kiterjesztések a következők:

  • A típusindexált értékeket a különböző Haskell típusú konstruktorokon (egység, primitív típusok, összegek, termékek és felhasználó által definiált típusú konstruktorok) indexelt értékként definiáljuk. Ezenkívül megadhatjuk a típusindexált értékek viselkedését egy adott konstruktor számára konstruktor esetek felhasználásával, és az egyik általános definíciót újra felhasználhatjuk a másikban alapértelmezett esetekkel.

Az így kapott indexelt érték bármilyen típusra specializálható.

  • A fajtaindexelt típusok a típusok között indexelt típusok, amelyek megadják a * és a k → k 'esetét. A példányokat úgy kapjuk meg, hogy a fajta indexelt típust alkalmazzuk egy fajtára..
  • Általános definíciók alkalmazhatók egy típusra vagy fajtára való alkalmazásukkal. Ezt hívjuk általános alkalmazásnak. Az eredmény egy típus vagy érték, attól függően, hogy melyik általános definíciót alkalmazzák.
  • Az általános absztrakció lehetővé teszi az általános definíciók definiálását egy (egy adott típusú) típusú paraméter elvonatkoztatásával.
  • A típusindexált típusok olyan típusok, amelyeket a típusépítők felett indexelnek. Ezek felhasználhatók típusok megadására a jobban érintett általános értékekhez. Az így kapott típusindexált típusok bármilyen típusra specializálódhatnak.

Például az egyenlőség függvénye a Generic Haskellben:

   type Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool
   type Eq {[ k -> l ]} t1 t2 = forall u1 u2. Eq {[ k ]} u1 u2 -> Eq {[ l ]} (t1 u1) (t2 u2)

   eq {| t :: k |} :: Eq {[ k ]} t t
   eq {| Unit |} _ _ = True
   eq {| :+: |} eqA eqB (Inl a1) (Inl a2) = eqA a1 a2
   eq {| :+: |} eqA eqB (Inr b1) (Inr b2) = eqB b1 b2
   eq {| :+: |} eqA eqB _ _ = False
   eq {| :*: |} eqA eqB (a1 :*: b1) (a2 :*: b2) = eqA a1 a2 && eqB b1 b2
   eq {| Int |} = (==)
   eq {| Char |} = (==)
   eq {| Bool |} = (==)

Clean[szerkesztés]

A Clean általános programozási alapú PolyP-t és az általános Haskell-t kínálja, amelyet a GHC> = 6.0 támogat. Ez természetük szerint paraméterez, de túlterhelést kínál.

Más nyelvek[szerkesztés]

Az ML család nyelvei támogatják az általános programozást a parametrikus polimorfizmus és az úgynevezett functor modulok révén. A Standard ML és az OCaml egyaránt biztosít funkciókat, amelyek hasonlóak az osztálysablonokhoz és Ada általános csomagjaihoz. A rendszer szintaktikai absztrakciói szintén kapcsolódnak a generikussághoz - ezek valójában a C ++ sablonok egy halmaza.

A Verilog modul tartalmazhat egy vagy több paramétert, amelyekhez a modul példányosításakor hozzárendelik tényleges értékeiket. Az egyik példa egy általános regiszter tömb, ahol a tömb szélességét egy paraméter adja meg. Egy ilyen tömb általános vezetékvektorral kombinálva tetszőleges bitszélességű általános puffert vagy memóriamodult készíthet egyetlen modul megvalósításából.

Az Adától származó VHDL általános képességekkel is rendelkezik.

#define cbrt(x) _Generic((x), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(x)

Jegyzetek[szerkesztés]

  1. Lee, Kent D.. Programming Languages: An Active Learning Approach. Springer Science & Business Media, 9–10. o. (2008. december 15.). ISBN 978-0-387-79422-8 
  2. Milner, R.; Morris, L.; Newey, M. (1975). "A Logic for Computable Functions with Reflexive and Polymorphic Types". Proceedings of the Conference on Proving and Improving Programs.
  3. Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns. Addison-Wesley. ISBN 0-201-63361-2.
  4. Stepanov, Alexander. Short History of STL 
  5. Stroustrup, Bjarne. Evolving a language in and for the real world: C++ 1991-2006. DOI: 10.1145/1238844.1238848 
  6. Lo Russo, Graziano: An Interview with A. Stepanov
  7. Lämmel, Ralf: Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming. Microsoft. (Hozzáférés: 2016. október 16.)
  8. Gabriel Dos Reis: What is Generic Programming? (preprint LCSD'05), 2005. [2005. december 25-i dátummal az eredetiből archiválva].

Fordítás[szerkesztés]

Ez a szócikk részben vagy egészben a Generic programming című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.

Kapcsolódó szócikkek[szerkesztés]

További információk[szerkesztés]

C++/D
C#/.NET
Delphi/Object Pascal
Eiffel
Haskell
Java